From de63ce45856bec5556d62f9f832df4191148c162 Mon Sep 17 00:00:00 2001 From: liquid-8 Date: Tue, 27 Jun 2023 22:22:36 +0300 Subject: [PATCH 01/18] v4 support pre-alpha --- .gitignore | 1 + .vs/slnx.sqlite | Bin 0 -> 98304 bytes uniswap/__init__.py | 1 + uniswap/uniswap4.py | 362 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 364 insertions(+) create mode 100644 .vs/slnx.sqlite create mode 100644 uniswap/uniswap4.py diff --git a/.gitignore b/.gitignore index 71a12ed6..c72a8d31 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ ENV/ # mkdocs documentation /site +/.vs/ProjectSettings.json diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..4aa01272d4cd812dd5c4a8548a89b623738fdf2d GIT binary patch literal 98304 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 literal 0 HcmV?d00001 diff --git a/uniswap/__init__.py b/uniswap/__init__.py index 53d0a5bb..4c23bbb3 100644 --- a/uniswap/__init__.py +++ b/uniswap/__init__.py @@ -1,3 +1,4 @@ from . import exceptions from .uniswap import Uniswap, _str_to_addr +from .uniswap4 import Uniswap4 from .cli import main diff --git a/uniswap/uniswap4.py b/uniswap/uniswap4.py new file mode 100644 index 00000000..32c6bf9b --- /dev/null +++ b/uniswap/uniswap4.py @@ -0,0 +1,362 @@ +import os +import time +import logging +import functools +from typing import List, Any, Optional, Union, Tuple, Dict + +from web3 import Web3 +from web3.eth import Contract +from web3.contract import ContractFunction +from web3.exceptions import BadFunctionCallOutput, ContractLogicError +from web3.types import ( + TxParams, + Wei, + Address, + ChecksumAddress, + Nonce, + HexBytes, +) + +from .types import AddressLike +from .token import ERC20Token +from .tokens import tokens, tokens_rinkeby +from .exceptions import InvalidToken, InsufficientBalance +from .util import ( + _str_to_addr, + _addr_to_str, + _validate_address, + _load_contract, + _load_contract_erc20, + is_same_address, +) +from .decorators import supports, check_approval +from .constants import ( + _netid_to_name, + _poolmanager_contract_addresses, + ETH_ADDRESS, +) + +logger = logging.getLogger(__name__) + + +class Uniswap4: + """ + Wrapper around Uniswap v4 contracts. + """ + + def __init__( + self, + address: Union[AddressLike, str, None], + private_key: Optional[str], + provider: str = None, + web3: Web3 = None, + default_slippage: float = 0.01, + poolmanager_contract_addr: str = None, + ) -> None: + """ + :param address: The public address of the ETH wallet to use. + :param private_key: The private key of the ETH wallet to use. + :param provider: Can be optionally set to a Web3 provider URI. If none set, will fall back to the PROVIDER environment variable, or web3 if set. + :param web3: Can be optionally set to a custom Web3 instance. + :param poolmanager_contract_addr: Can be optionally set to override the address of the PoolManager contract. + """ + self.address: AddressLike = _str_to_addr( + address or "0x0000000000000000000000000000000000000000" + ) + self.private_key = ( + private_key + or "0x0000000000000000000000000000000000000000000000000000000000000000" + ) + + if web3: + self.w3 = web3 + else: + # Initialize web3. Extra provider for testing. + self.provider = provider or os.environ["PROVIDER"] + self.w3 = Web3( + Web3.HTTPProvider(self.provider, request_kwargs={"timeout": 60}) + ) + + netid = int(self.w3.net.version) + if netid in _netid_to_name: + self.network = _netid_to_name[netid] + else: + raise Exception(f"Unknown netid: {netid}") + logger.info(f"Using {self.w3} ('{self.network}')") + + self.last_nonce: Nonce = self.w3.eth.get_transaction_count(self.address) + + if poolmanager_contract_addr is None: + poolmanager_contract_addr = _poolmanager_contract_addresses[self.network] + + self.poolmanager_contract = _load_contract( + self.w3, + abi_name="uniswap-v4/poolmanager", + address=_str_to_addr(poolmanager_contract_addr), + ) + + if hasattr(self, "poolmanager_contract"): + logger.info(f"Using factory contract: {self.poolmanager_contract}") + + # ------ Market -------------------------------------------------------------------- + + def get_price( + self, + token0: AddressLike, # input token + token1: AddressLike, # output token + qty: int, + fee: int, + route: Optional[List[AddressLike]] = None, + zero_to_one: bool = true, + ) -> int: + """ + :if `zero_to_one` is true: given `qty` amount of the input `token0`, returns the maximum output amount of output `token1`. + :if `zero_to_one` is false: returns the minimum amount of `token0` required to buy `qty` amount of `token1`. + """ + + # WIP + + return 0 + + # ------ Make Trade ---------------------------------------------------------------- + def make_trade( + self, + currency0: ERC20Token, + currency1: ERC20Token, + qty: Union[int, Wei], + fee: int, + tick_spacing: int, + sqrt_price_limit_x96: int = 0, + zero_for_one: bool = true, + hooks: AddressLike = ETH, + ) -> HexBytes: + """ + :Swap against the given pool + : + :`currency0`:The lower currency of the pool, sorted numerically + :`currency1`:The higher currency of the pool, sorted numerically + :`fee`: The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees. + :`tickSpacing`: Ticks that involve positions must be a multiple of tick spacing + :`hooks`: The hooks of the pool + :if `zero_for_one` is true: make a trade by defining the qty of the input token. + :if `zero_for_one` is false: make a trade by defining the qty of the output token. + """ + if currency0 == currency1: + raise ValueError + + pool_key = { + "currency0": currency0.address, + "currency1": currency1.address, + "fee": fee, + "tickSpacing": tick_spacing, + "hooks": hooks, + } + + swap_params = { + "zeroForOne": zero_for_one, + "amountSpecified": qty, + "sqrtPriceLimitX96": sqrt_price_limit_x96, + } + + return self._build_and_send_tx( + self.router.functions.swap( + { + "key": pool_key, + "params": swap_params, + } + ), + self._get_tx_params(value=qty), + ) + + # ------ Wallet balance ------------------------------------------------------------ + def get_eth_balance(self) -> Wei: + """Get the balance of ETH for your address.""" + return self.w3.eth.get_balance(self.address) + + def get_token_balance(self, token: AddressLike) -> int: + """Get the balance of a token for your address.""" + _validate_address(token) + if _addr_to_str(token) == ETH_ADDRESS: + return self.get_eth_balance() + erc20 = _load_contract_erc20(self.w3, token) + balance: int = erc20.functions.balanceOf(self.address).call() + return balance + + # ------ Liquidity ----------------------------------------------------------------- + def initialize( + self, + currency0: ERC20Token, + currency1: ERC20Token, + qty: Union[int, Wei], + fee: int, + tick_spacing: int, + hooks: AddressLike, + sqrt_price_limit_x96: int, + ) -> HexBytes: + """ + :Initialize the state for a given pool ID + : + :`currency0`:The lower currency of the pool, sorted numerically + :`currency1`:The higher currency of the pool, sorted numerically + :`fee`: The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees. + :`tickSpacing`: Ticks that involve positions must be a multiple of tick spacing + :`hooks`: The hooks of the pool + """ + if currency0 == currency1: + raise ValueError + + pool_key = { + "currency0": currency0.address, + "currency1": currency1.address, + "fee": fee, + "tickSpacing": tick_spacing, + "hooks": hooks, + } + + return self._build_and_send_tx( + self.router.functions.initialize( + { + "key": pool_key, + "sqrtPriceX96": sqrt_price_limit_x96, + } + ), + self._get_tx_params(value=qty), + ) + + def modify_position( + self, + currency0: ERC20Token, + currency1: ERC20Token, + qty: Union[int, Wei], + fee: int, + tick_spacing: int, + tick_upper: int, + tick_lower: int, + hooks: AddressLike, + ) -> HexBytes: + if currency0 == currency1: + raise ValueError + + pool_key = { + "currency0": currency0.address, + "currency1": currency1.address, + "fee": fee, + "tickSpacing": tick_spacing, + "hooks": hooks, + } + + modify_position_params = { + "tickLower": tick_lower, + "tickUpper": tick_upper, + "liquidityDelta": qty, + } + + return self._build_and_send_tx( + self.router.functions.modifyPosition( + { + "key": pool_key, + "params": modify_position_params, + } + ), + self._get_tx_params(value=qty), + ) + + # ------ Approval Utils ------------------------------------------------------------ + def approve(self, token: AddressLike, max_approval: Optional[int] = None) -> None: + """Give an exchange/router max approval of a token.""" + max_approval = self.max_approval_int if not max_approval else max_approval + contract_addr = ( + self._exchange_address_from_token(token) + if self.version == 1 + else self.router_address + ) + function = _load_contract_erc20(self.w3, token).functions.approve( + contract_addr, max_approval + ) + logger.warning(f"Approving {_addr_to_str(token)}...") + tx = self._build_and_send_tx(function) + self.w3.eth.wait_for_transaction_receipt(tx, timeout=6000) + + # Add extra sleep to let tx propogate correctly + time.sleep(1) + + # ------ Tx Utils ------------------------------------------------------------------ + def _deadline(self) -> int: + """Get a predefined deadline. 10min by default (same as the Uniswap SDK).""" + return int(time.time()) + 10 * 60 + + def _build_and_send_tx( + self, function: ContractFunction, tx_params: Optional[TxParams] = None + ) -> HexBytes: + """Build and send a transaction.""" + if not tx_params: + tx_params = self._get_tx_params() + transaction = function.buildTransaction(tx_params) + # Uniswap3 uses 20% margin for transactions + transaction["gas"] = Wei(int(self.w3.eth.estimate_gas(transaction) * 1.2)) + signed_txn = self.w3.eth.account.sign_transaction( + transaction, private_key=self.private_key + ) + # TODO: This needs to get more complicated if we want to support replacing a transaction + # FIXME: This does not play nice if transactions are sent from other places using the same wallet. + try: + return self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) + finally: + logger.debug(f"nonce: {tx_params['nonce']}") + self.last_nonce = Nonce(tx_params["nonce"] + 1) + + def _get_tx_params(self, value: Wei = Wei(0)) -> TxParams: + """Get generic transaction parameters.""" + return { + "from": _addr_to_str(self.address), + "value": value, + "nonce": max( + self.last_nonce, self.w3.eth.get_transaction_count(self.address) + ), + } + + # ------ Helpers ------------------------------------------------------------ + + def get_token(self, address: AddressLike, abi_name: str = "erc20") -> ERC20Token: + """ + Retrieves metadata from the ERC20 contract of a given token, like its name, symbol, and decimals. + """ + # FIXME: This function should always return the same output for the same input + # and would therefore benefit from caching + if address == ETH_ADDRESS: + return ERC20Token("ETH", ETH_ADDRESS, "Ether", 18) + token_contract = _load_contract(self.w3, abi_name, address=address) + try: + _name = token_contract.functions.name().call() + _symbol = token_contract.functions.symbol().call() + decimals = token_contract.functions.decimals().call() + except Exception as e: + logger.warning( + f"Exception occurred while trying to get token {_addr_to_str(address)}: {e}" + ) + raise InvalidToken(address) + try: + name = _name.decode() + except: + name = _name + try: + symbol = _symbol.decode() + except: + symbol = _symbol + return ERC20Token(symbol, address, name, decimals) + + # ------ Test utilities ------------------------------------------------------------ + + def _get_token_addresses(self) -> Dict[str, ChecksumAddress]: + """ + Returns a dict with addresses for tokens for the current net. + Used in testing. + """ + netid = int(self.w3.net.version) + netname = _netid_to_name[netid] + if netname == "mainnet": + return tokens + elif netname == "rinkeby": + return tokens_rinkeby + else: + raise Exception(f"Unknown net '{netname}'") From fa7563256d3c4955035dc6727ad1e185d1e978c6 Mon Sep 17 00:00:00 2001 From: liquid-8 Date: Tue, 27 Jun 2023 22:34:54 +0300 Subject: [PATCH 02/18] v4 support pre-alpha --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c72a8d31..702ece3f 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,4 @@ ENV/ # mkdocs documentation /site -/.vs/ProjectSettings.json +/.vs From 0f0a1898be361da8e50a4b858e485b29e16a309b Mon Sep 17 00:00:00 2001 From: liquid-8 Date: Tue, 27 Jun 2023 22:40:10 +0300 Subject: [PATCH 03/18] Revert "v4 support pre-alpha" This reverts commit fa7563256d3c4955035dc6727ad1e185d1e978c6. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 702ece3f..c72a8d31 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,4 @@ ENV/ # mkdocs documentation /site -/.vs +/.vs/ProjectSettings.json From d9b28e84f92f9a002c6f347ac79254fd08290ade Mon Sep 17 00:00:00 2001 From: liquid-8 Date: Tue, 27 Jun 2023 22:40:46 +0300 Subject: [PATCH 04/18] Revert "v4 support pre-alpha" This reverts commit de63ce45856bec5556d62f9f832df4191148c162. --- .gitignore | 1 - .vs/slnx.sqlite | Bin 98304 -> 0 bytes uniswap/__init__.py | 1 - uniswap/uniswap4.py | 362 -------------------------------------------- 4 files changed, 364 deletions(-) delete mode 100644 .vs/slnx.sqlite delete mode 100644 uniswap/uniswap4.py diff --git a/.gitignore b/.gitignore index c72a8d31..71a12ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,3 @@ ENV/ # mkdocs documentation /site -/.vs/ProjectSettings.json diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite deleted file mode 100644 index 4aa01272d4cd812dd5c4a8548a89b623738fdf2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98304 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 diff --git a/uniswap/__init__.py b/uniswap/__init__.py index 4c23bbb3..53d0a5bb 100644 --- a/uniswap/__init__.py +++ b/uniswap/__init__.py @@ -1,4 +1,3 @@ from . import exceptions from .uniswap import Uniswap, _str_to_addr -from .uniswap4 import Uniswap4 from .cli import main diff --git a/uniswap/uniswap4.py b/uniswap/uniswap4.py deleted file mode 100644 index 32c6bf9b..00000000 --- a/uniswap/uniswap4.py +++ /dev/null @@ -1,362 +0,0 @@ -import os -import time -import logging -import functools -from typing import List, Any, Optional, Union, Tuple, Dict - -from web3 import Web3 -from web3.eth import Contract -from web3.contract import ContractFunction -from web3.exceptions import BadFunctionCallOutput, ContractLogicError -from web3.types import ( - TxParams, - Wei, - Address, - ChecksumAddress, - Nonce, - HexBytes, -) - -from .types import AddressLike -from .token import ERC20Token -from .tokens import tokens, tokens_rinkeby -from .exceptions import InvalidToken, InsufficientBalance -from .util import ( - _str_to_addr, - _addr_to_str, - _validate_address, - _load_contract, - _load_contract_erc20, - is_same_address, -) -from .decorators import supports, check_approval -from .constants import ( - _netid_to_name, - _poolmanager_contract_addresses, - ETH_ADDRESS, -) - -logger = logging.getLogger(__name__) - - -class Uniswap4: - """ - Wrapper around Uniswap v4 contracts. - """ - - def __init__( - self, - address: Union[AddressLike, str, None], - private_key: Optional[str], - provider: str = None, - web3: Web3 = None, - default_slippage: float = 0.01, - poolmanager_contract_addr: str = None, - ) -> None: - """ - :param address: The public address of the ETH wallet to use. - :param private_key: The private key of the ETH wallet to use. - :param provider: Can be optionally set to a Web3 provider URI. If none set, will fall back to the PROVIDER environment variable, or web3 if set. - :param web3: Can be optionally set to a custom Web3 instance. - :param poolmanager_contract_addr: Can be optionally set to override the address of the PoolManager contract. - """ - self.address: AddressLike = _str_to_addr( - address or "0x0000000000000000000000000000000000000000" - ) - self.private_key = ( - private_key - or "0x0000000000000000000000000000000000000000000000000000000000000000" - ) - - if web3: - self.w3 = web3 - else: - # Initialize web3. Extra provider for testing. - self.provider = provider or os.environ["PROVIDER"] - self.w3 = Web3( - Web3.HTTPProvider(self.provider, request_kwargs={"timeout": 60}) - ) - - netid = int(self.w3.net.version) - if netid in _netid_to_name: - self.network = _netid_to_name[netid] - else: - raise Exception(f"Unknown netid: {netid}") - logger.info(f"Using {self.w3} ('{self.network}')") - - self.last_nonce: Nonce = self.w3.eth.get_transaction_count(self.address) - - if poolmanager_contract_addr is None: - poolmanager_contract_addr = _poolmanager_contract_addresses[self.network] - - self.poolmanager_contract = _load_contract( - self.w3, - abi_name="uniswap-v4/poolmanager", - address=_str_to_addr(poolmanager_contract_addr), - ) - - if hasattr(self, "poolmanager_contract"): - logger.info(f"Using factory contract: {self.poolmanager_contract}") - - # ------ Market -------------------------------------------------------------------- - - def get_price( - self, - token0: AddressLike, # input token - token1: AddressLike, # output token - qty: int, - fee: int, - route: Optional[List[AddressLike]] = None, - zero_to_one: bool = true, - ) -> int: - """ - :if `zero_to_one` is true: given `qty` amount of the input `token0`, returns the maximum output amount of output `token1`. - :if `zero_to_one` is false: returns the minimum amount of `token0` required to buy `qty` amount of `token1`. - """ - - # WIP - - return 0 - - # ------ Make Trade ---------------------------------------------------------------- - def make_trade( - self, - currency0: ERC20Token, - currency1: ERC20Token, - qty: Union[int, Wei], - fee: int, - tick_spacing: int, - sqrt_price_limit_x96: int = 0, - zero_for_one: bool = true, - hooks: AddressLike = ETH, - ) -> HexBytes: - """ - :Swap against the given pool - : - :`currency0`:The lower currency of the pool, sorted numerically - :`currency1`:The higher currency of the pool, sorted numerically - :`fee`: The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees. - :`tickSpacing`: Ticks that involve positions must be a multiple of tick spacing - :`hooks`: The hooks of the pool - :if `zero_for_one` is true: make a trade by defining the qty of the input token. - :if `zero_for_one` is false: make a trade by defining the qty of the output token. - """ - if currency0 == currency1: - raise ValueError - - pool_key = { - "currency0": currency0.address, - "currency1": currency1.address, - "fee": fee, - "tickSpacing": tick_spacing, - "hooks": hooks, - } - - swap_params = { - "zeroForOne": zero_for_one, - "amountSpecified": qty, - "sqrtPriceLimitX96": sqrt_price_limit_x96, - } - - return self._build_and_send_tx( - self.router.functions.swap( - { - "key": pool_key, - "params": swap_params, - } - ), - self._get_tx_params(value=qty), - ) - - # ------ Wallet balance ------------------------------------------------------------ - def get_eth_balance(self) -> Wei: - """Get the balance of ETH for your address.""" - return self.w3.eth.get_balance(self.address) - - def get_token_balance(self, token: AddressLike) -> int: - """Get the balance of a token for your address.""" - _validate_address(token) - if _addr_to_str(token) == ETH_ADDRESS: - return self.get_eth_balance() - erc20 = _load_contract_erc20(self.w3, token) - balance: int = erc20.functions.balanceOf(self.address).call() - return balance - - # ------ Liquidity ----------------------------------------------------------------- - def initialize( - self, - currency0: ERC20Token, - currency1: ERC20Token, - qty: Union[int, Wei], - fee: int, - tick_spacing: int, - hooks: AddressLike, - sqrt_price_limit_x96: int, - ) -> HexBytes: - """ - :Initialize the state for a given pool ID - : - :`currency0`:The lower currency of the pool, sorted numerically - :`currency1`:The higher currency of the pool, sorted numerically - :`fee`: The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees. - :`tickSpacing`: Ticks that involve positions must be a multiple of tick spacing - :`hooks`: The hooks of the pool - """ - if currency0 == currency1: - raise ValueError - - pool_key = { - "currency0": currency0.address, - "currency1": currency1.address, - "fee": fee, - "tickSpacing": tick_spacing, - "hooks": hooks, - } - - return self._build_and_send_tx( - self.router.functions.initialize( - { - "key": pool_key, - "sqrtPriceX96": sqrt_price_limit_x96, - } - ), - self._get_tx_params(value=qty), - ) - - def modify_position( - self, - currency0: ERC20Token, - currency1: ERC20Token, - qty: Union[int, Wei], - fee: int, - tick_spacing: int, - tick_upper: int, - tick_lower: int, - hooks: AddressLike, - ) -> HexBytes: - if currency0 == currency1: - raise ValueError - - pool_key = { - "currency0": currency0.address, - "currency1": currency1.address, - "fee": fee, - "tickSpacing": tick_spacing, - "hooks": hooks, - } - - modify_position_params = { - "tickLower": tick_lower, - "tickUpper": tick_upper, - "liquidityDelta": qty, - } - - return self._build_and_send_tx( - self.router.functions.modifyPosition( - { - "key": pool_key, - "params": modify_position_params, - } - ), - self._get_tx_params(value=qty), - ) - - # ------ Approval Utils ------------------------------------------------------------ - def approve(self, token: AddressLike, max_approval: Optional[int] = None) -> None: - """Give an exchange/router max approval of a token.""" - max_approval = self.max_approval_int if not max_approval else max_approval - contract_addr = ( - self._exchange_address_from_token(token) - if self.version == 1 - else self.router_address - ) - function = _load_contract_erc20(self.w3, token).functions.approve( - contract_addr, max_approval - ) - logger.warning(f"Approving {_addr_to_str(token)}...") - tx = self._build_and_send_tx(function) - self.w3.eth.wait_for_transaction_receipt(tx, timeout=6000) - - # Add extra sleep to let tx propogate correctly - time.sleep(1) - - # ------ Tx Utils ------------------------------------------------------------------ - def _deadline(self) -> int: - """Get a predefined deadline. 10min by default (same as the Uniswap SDK).""" - return int(time.time()) + 10 * 60 - - def _build_and_send_tx( - self, function: ContractFunction, tx_params: Optional[TxParams] = None - ) -> HexBytes: - """Build and send a transaction.""" - if not tx_params: - tx_params = self._get_tx_params() - transaction = function.buildTransaction(tx_params) - # Uniswap3 uses 20% margin for transactions - transaction["gas"] = Wei(int(self.w3.eth.estimate_gas(transaction) * 1.2)) - signed_txn = self.w3.eth.account.sign_transaction( - transaction, private_key=self.private_key - ) - # TODO: This needs to get more complicated if we want to support replacing a transaction - # FIXME: This does not play nice if transactions are sent from other places using the same wallet. - try: - return self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) - finally: - logger.debug(f"nonce: {tx_params['nonce']}") - self.last_nonce = Nonce(tx_params["nonce"] + 1) - - def _get_tx_params(self, value: Wei = Wei(0)) -> TxParams: - """Get generic transaction parameters.""" - return { - "from": _addr_to_str(self.address), - "value": value, - "nonce": max( - self.last_nonce, self.w3.eth.get_transaction_count(self.address) - ), - } - - # ------ Helpers ------------------------------------------------------------ - - def get_token(self, address: AddressLike, abi_name: str = "erc20") -> ERC20Token: - """ - Retrieves metadata from the ERC20 contract of a given token, like its name, symbol, and decimals. - """ - # FIXME: This function should always return the same output for the same input - # and would therefore benefit from caching - if address == ETH_ADDRESS: - return ERC20Token("ETH", ETH_ADDRESS, "Ether", 18) - token_contract = _load_contract(self.w3, abi_name, address=address) - try: - _name = token_contract.functions.name().call() - _symbol = token_contract.functions.symbol().call() - decimals = token_contract.functions.decimals().call() - except Exception as e: - logger.warning( - f"Exception occurred while trying to get token {_addr_to_str(address)}: {e}" - ) - raise InvalidToken(address) - try: - name = _name.decode() - except: - name = _name - try: - symbol = _symbol.decode() - except: - symbol = _symbol - return ERC20Token(symbol, address, name, decimals) - - # ------ Test utilities ------------------------------------------------------------ - - def _get_token_addresses(self) -> Dict[str, ChecksumAddress]: - """ - Returns a dict with addresses for tokens for the current net. - Used in testing. - """ - netid = int(self.w3.net.version) - netname = _netid_to_name[netid] - if netname == "mainnet": - return tokens - elif netname == "rinkeby": - return tokens_rinkeby - else: - raise Exception(f"Unknown net '{netname}'") From 18fad99c8514a6380d9b17ebda72552482561b4e Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:45:39 +0200 Subject: [PATCH 05/18] Update __init__.py --- uniswap/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/uniswap/__init__.py b/uniswap/__init__.py index a1612f65..f9f37116 100644 --- a/uniswap/__init__.py +++ b/uniswap/__init__.py @@ -1,5 +1,6 @@ from . import exceptions from .cli import main from .uniswap import Uniswap, _str_to_addr +from .uniswap4 import Uniswap4 __all__ = ["Uniswap", "exceptions", "_str_to_addr", "main"] From 2397805f562ad09c22b4c211fcbdd41dd83a5323 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:48:12 +0200 Subject: [PATCH 06/18] Add files via upload --- uniswap/configs/evmuniV4_chains.ini | 16 + uniswap/configs/evmuniV4_permit2.ini | 16 + uniswap/configs/evmuniV4_quoter.ini | 16 + uniswap/configs/evmuniV4_router.ini | 16 + uniswap/configs/evmuniV4_stateview.ini | 16 + uniswap/uni4base.py | 42 ++ uniswap/uniswap-v4/permit2.abi | 901 +++++++++++++++++++++++++ uniswap/uniswap-v4/quoter.abi | 637 +++++++++++++++++ uniswap/uniswap-v4/router.abi | 1 + uniswap/uniswap-v4/stateview.abi | 365 ++++++++++ uniswap/uniswap4.py | 457 +++++++++++++ 11 files changed, 2483 insertions(+) create mode 100644 uniswap/configs/evmuniV4_chains.ini create mode 100644 uniswap/configs/evmuniV4_permit2.ini create mode 100644 uniswap/configs/evmuniV4_quoter.ini create mode 100644 uniswap/configs/evmuniV4_router.ini create mode 100644 uniswap/configs/evmuniV4_stateview.ini create mode 100644 uniswap/uni4base.py create mode 100644 uniswap/uniswap-v4/permit2.abi create mode 100644 uniswap/uniswap-v4/quoter.abi create mode 100644 uniswap/uniswap-v4/router.abi create mode 100644 uniswap/uniswap-v4/stateview.abi create mode 100644 uniswap/uniswap4.py diff --git a/uniswap/configs/evmuniV4_chains.ini b/uniswap/configs/evmuniV4_chains.ini new file mode 100644 index 00000000..4739b909 --- /dev/null +++ b/uniswap/configs/evmuniV4_chains.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=chain name +[settings] +1=MAINNET +130=Unichain +10=Optimism +8453=Base +42161=Arbitrum +137=Polygon +81457=Blast +7777777=Zora +480=Worldchain +57073=Ink +1868=Soneium +43114=Avalanche +56=BNB diff --git a/uniswap/configs/evmuniV4_permit2.ini b/uniswap/configs/evmuniV4_permit2.ini new file mode 100644 index 00000000..c6ee0ede --- /dev/null +++ b/uniswap/configs/evmuniV4_permit2.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x000000000022D473030F116dDEE9F6B43aC78BA3 +130=0x000000000022D473030F116dDEE9F6B43aC78BA3 +10=0x000000000022D473030F116dDEE9F6B43aC78BA3 +8453=0x000000000022D473030F116dDEE9F6B43aC78BA3 +42161=0x000000000022D473030F116dDEE9F6B43aC78BA3 +137=0x000000000022D473030F116dDEE9F6B43aC78BA3 +81457=0x000000000022D473030F116dDEE9F6B43aC78BA3 +7777777=0x000000000022D473030F116dDEE9F6B43aC78BA3 +480=0x000000000022D473030F116dDEE9F6B43aC78BA3 +57073=0x000000000022D473030F116dDEE9F6B43aC78BA3 +1868=0x000000000022D473030F116dDEE9F6B43aC78BA3 +43114=0x000000000022D473030F116dDEE9F6B43aC78BA3 +56=0x000000000022D473030F116dDEE9F6B43aC78BA3 diff --git a/uniswap/configs/evmuniV4_quoter.ini b/uniswap/configs/evmuniV4_quoter.ini new file mode 100644 index 00000000..f6f27dd8 --- /dev/null +++ b/uniswap/configs/evmuniV4_quoter.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=quoter contract address +[settings] +1=0x52f0e24d1c21c8a0cb1e5a5dd6198556bd9e1203 +130=0x333e3c607b141b18ff6de9f258db6e77fe7491e0 +10=0x1f3131a13296fb91c90870043742c3cdbff1a8d7 +8453=0x0d5e0f971ed27fbff6c2837bf31316121532048d +42161=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +137=0xb3d5c3dfc3a7aebff71895a7191796bffc2c81b9 +81457=0x6f71cdcb0d119ff72c6eb501abceb576fbf62bcf +7777777=0x5edaccc0660e0a2c44b06e07ce8b915e625dc2c6 +480=0x55d235b3ff2daf7c3ede0defc9521f1d6fe6c5c0 +57073=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +1868=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +43114=0xbe40675bb704506a3c2ccfb762dcfd1e979845c2 +56=0x9f75dd27d6664c475b90e105573e550ff69437b0 \ No newline at end of file diff --git a/uniswap/configs/evmuniV4_router.ini b/uniswap/configs/evmuniV4_router.ini new file mode 100644 index 00000000..0cb1f7dd --- /dev/null +++ b/uniswap/configs/evmuniV4_router.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x66a9893cc07d91d95644aedd05d03f95e1dba8af +130=0xef740bf23acae26f6492b10de645d6b98dc8eaf3 +10=0x851116d9223fabed8e56c0e6b8ad0c31d98b3507 +8453=0x6ff5693b99212da76ad316178a184ab56d299b43 +42161=0xa51afafe0263b40edaef0df8781ea9aa03e381a3 +137=0x1095692a6237d83c6a72f3f5efedb9a670c49223 +81457=0xeabbcb3e8e415306207ef514f660a3f820025be3 +7777777=0x3315ef7ca28db74abadc6c44570efdf06b04b020 +480=0x8ac7bee993bb44dab564ea4bc9ea67bf9eb5e743 +57073=0x112908dac86e20e7241b0927479ea3bf935d1fa0 +1868=0x4cded7edf52c8aa5259a54ec6a3ce7c6d2a455df +43114=0x94b75331ae8d42c1b61065089b7d48fe14aa73b7 +56=0x1906c1d672b88cd1b9ac7593301ca990f94eae07 diff --git a/uniswap/configs/evmuniV4_stateview.ini b/uniswap/configs/evmuniV4_stateview.ini new file mode 100644 index 00000000..7bb9d6be --- /dev/null +++ b/uniswap/configs/evmuniV4_stateview.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x7ffe42c4a5deea5b0fec41c94c136cf115597227 +130=0x86e8631a016f9068c3f085faf484ee3f5fdee8f2 +10=0xc18a3169788f4f75a170290584eca6395c75ecdb +8453=0xa3c0c9b65bad0b08107aa264b0f3db444b867a71 +42161=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +137=0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a +81457=0x12a88ae16f46dce4e8b15368008ab3380885df30 +7777777=0x385785af07d63b50d0a0ea57c4ff89d06adf7328 +480=0x51d394718bc09297262e368c1a481217fdeb71eb +57073=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +1868=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +43114=0xc3c9e198c735a4b97e3e683f391ccbdd60b69286 +56=0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4 diff --git a/uniswap/uni4base.py b/uniswap/uni4base.py new file mode 100644 index 00000000..615a3e22 --- /dev/null +++ b/uniswap/uni4base.py @@ -0,0 +1,42 @@ +import os +import json +import time +import logging +import functools +from typing import List, Any, Optional, Callable, Union, Tuple, Dict + +from web3 import Web3 +from web3.types import ( + TxParams, + Wei, + Address, + ChecksumAddress, + ENS, + Nonce, + HexBytes, +) +from eth_utils import is_same_address +from eth_typing import AnyAddress + +ZERO_HOOK = "0x0000000000000000000000000000000000000000" +ETH_ADDRESS = "0x0000000000000000000000000000000000000000" +WRAPPED_ETH_ADDRESS = "0xc207eb4dF2E25c180902257aF349d841022561E8" + + +class pool_key: + currency0 : str + currency1 : str + fee : int + tick_spacing : int + hooks : str + + +class InvalidToken(Exception): + def __init__(self, address: Any) -> None: + Exception.__init__(self, f"Invalid token address: {address}") + + +class InsufficientBalance(Exception): + def __init__(self, had: int, needed: int) -> None: + Exception.__init__(self, f"Insufficient balance. Had {had}, needed {needed}") + diff --git a/uniswap/uniswap-v4/permit2.abi b/uniswap/uniswap-v4/permit2.abi new file mode 100644 index 00000000..2f0281eb --- /dev/null +++ b/uniswap/uniswap-v4/permit2.abi @@ -0,0 +1,901 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "AllowanceExpired", + "type": "error" + }, + { + "inputs": [], + "name": "ExcessiveInvalidation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxAmount", + "type": "uint256" + } + ], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidContractSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidNonce", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSigner", + "type": "error" + }, + { + "inputs": [], + "name": "LengthMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + } + ], + "name": "SignatureExpired", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "Lockdown", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "newNonce", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "oldNonce", + "type": "uint48" + } + ], + "name": "NonceInvalidation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "name": "Permit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "word", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mask", + "type": "uint256" + } + ], + "name": "UnorderedNonceInvalidation", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint48", + "name": "newNonce", + "type": "uint48" + } + ], + "name": "invalidateNonces", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "wordPos", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mask", + "type": "uint256" + } + ], + "name": "invalidateUnorderedNonces", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "internalType": "struct IAllowanceTransfer.TokenSpenderPair[]", + "name": "approvals", + "type": "tuple[]" + } + ], + "name": "lockdown", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "nonceBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "internalType": "struct IAllowanceTransfer.PermitDetails[]", + "name": "details", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sigDeadline", + "type": "uint256" + } + ], + "internalType": "struct IAllowanceTransfer.PermitBatch", + "name": "permitBatch", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "internalType": "struct IAllowanceTransfer.PermitDetails", + "name": "details", + "type": "tuple" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sigDeadline", + "type": "uint256" + } + ], + "internalType": "struct IAllowanceTransfer.PermitSingle", + "name": "permitSingle", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions", + "name": "permitted", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails", + "name": "transferDetails", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions[]", + "name": "permitted", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions", + "name": "permitted", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails", + "name": "transferDetails", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "witness", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "witnessTypeString", + "type": "string" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitWitnessTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions[]", + "name": "permitted", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "witness", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "witnessTypeString", + "type": "string" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitWitnessTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "internalType": "struct IAllowanceTransfer.AllowanceTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/uniswap-v4/quoter.abi b/uniswap/uniswap-v4/quoter.abi new file mode 100644 index 00000000..f38100bc --- /dev/null +++ b/uniswap/uniswap-v4/quoter.abi @@ -0,0 +1,637 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPoolManager", + "name": "_poolManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "NotEnoughLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "NotPoolManager", + "type": "error" + }, + { + "inputs": [], + "name": "NotSelf", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "QuoteSwap", + "type": "error" + }, + { + "inputs": [], + "name": "UnexpectedCallSuccess", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "revertData", + "type": "bytes" + } + ], + "name": "UnexpectedRevertBytes", + "type": "error" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactInput", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactInputSingle", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactOutput", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactOutputSingle", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "poolManager", + "outputs": [ + { + "internalType": "contract IPoolManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactInput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactInputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactOutputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlockCallback", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/uniswap-v4/router.abi b/uniswap/uniswap-v4/router.abi new file mode 100644 index 00000000..2474909f --- /dev/null +++ b/uniswap/uniswap-v4/router.abi @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"},{"internalType":"address","name":"v4PoolManager","type":"address"},{"internalType":"address","name":"v3NFTPositionManager","type":"address"},{"internalType":"address","name":"v4PositionManager","type":"address"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotNegative","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotPositive","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[{"internalType":"bytes4","name":"action","type":"bytes4"}],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidEthSender","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotAuthorizedForToken","type":"error"},{"inputs":[],"name":"NotPoolManager","type":"error"},{"inputs":[],"name":"OnlyMintAllowed","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[{"internalType":"uint256","name":"action","type":"uint256"}],"name":"UnsupportedAction","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountOutReceived","type":"uint256"},{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"V4TooLittleReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxAmountInRequested","type":"uint256"},{"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"V4TooMuchRequested","type":"error"},{"inputs":[],"name":"V3_POSITION_MANAGER","outputs":[{"internalType":"contract INonfungiblePositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"V4_POSITION_MANAGER","outputs":[{"internalType":"contract IPositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"unlockCallback","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/uniswap/uniswap-v4/stateview.abi b/uniswap/uniswap-v4/stateview.abi new file mode 100644 index 00000000..19d58662 --- /dev/null +++ b/uniswap/uniswap-v4/stateview.abi @@ -0,0 +1,365 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPoolManager", + "name": "_poolManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getFeeGrowthGlobals", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthGlobal0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthGlobal1", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "getFeeGrowthInside", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthInside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "positionId", + "type": "bytes32" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "positionId", + "type": "bytes32" + } + ], + "name": "getPositionLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getSlot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "lpFee", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int16", + "name": "tick", + "type": "int16" + } + ], + "name": "getTickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "tickBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickFeeGrowthOutside", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolManager", + "outputs": [ + { + "internalType": "contract IPoolManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/uniswap4.py b/uniswap/uniswap4.py new file mode 100644 index 00000000..2b5faab2 --- /dev/null +++ b/uniswap/uniswap4.py @@ -0,0 +1,457 @@ +from web3 import Web3 +import eth_abi.abi +from web3.contract import Contract +from web3.contract.contract import ContractFunction +from eth_typing import AnyAddress +from eth_abi.codec import ABICodec +from eth_abi import encode +from eth_abi.packed import encode_packed +import os +import fnmatch +import configparser +from uni4base import * +import logging +from decimal import Decimal +##from colorama import Fore +##from colorama import Style +from typing import List, Any, Optional, Callable, Union, Tuple, Dict +from web3.types import ( + TxParams, + Wei, + Address, + ChecksumAddress, + ENS, + Nonce, + HexBytes, +) +import json +import ctypes + + + +AddressLike = Union[Address, ChecksumAddress, ENS] +_netid_to_name = {1000: "mainnet", 1001: "nile"} +with open(os.path.abspath(f"assets\\erc20.abi")) as f: + erc20_ABI : str = json.load(f) + +eth_by_dex = { + "UNISWAPV4" : ETH_ADDRESS, +} +transfer_by_dex = { + "UNISWAPV4" : WRAPPED_ETH_ADDRESS, +} +weth_by_dex = { + "UNISWAPV4" : WRAPPED_ETH_ADDRESS, +} + +def _addr_to_str(a: AddressLike) -> str: + if isinstance(a, bytes): + # Address or ChecksumAddress + addr : str = Web3.to_checksum_address("0x" + bytes(a).hex()) + return addr + elif isinstance(a, str): + if a.endswith(".ens"): + # Address is ENS + raise Exception("ENS not supported for this operation") + elif a.startswith("0x"): + addr = Web3.to_checksum_address(a) + return addr + else: + raise InvalidToken(a) + +def _str_to_addr(s: str) -> AddressLike: + if s.startswith("0x"): + return Address(bytes.fromhex(s[2:])) + elif s.endswith(".ens"): + return ENS(s) + else: + raise Exception("Could't convert string {s} to AddressLike") + +def is_version3(dex: str) -> bool: + if "V3" in dex: + return True + else: + return False + +class Uniswap4(): + def __init__(self, + address: Union[str, AddressLike], + private_key: str, + provider: str=None, + web3: Web3=None, + version: int=4, + max_slippage: float=0.1, + max_gas: float=250001.0, + max_gprice: float=18.0, + dex_name: str="", + london_fork: int=1, + max_priorityfee: float=2.0,) -> None: + + self.address : AddressLike = _str_to_addr(address) if isinstance(address, str) else address + self.private_key = private_key + self.version = version + + self.max_slippage = max_slippage + + if web3: + self.w3 = web3 + else: + self.provider = provider or os.environ["PROVIDER"] + self.w3 = Web3(Web3.HTTPProvider(self.provider, request_kwargs={"timeout": 60})) + + self.last_nonce : Nonce = self.w3.eth.get_transaction_count(self.address) + + # This code automatically approves you for trading on the exchange. + # max_approval is to allow the contract to exchange on your behalf. + # max_approval_check checks that current approval is above a reasonable + # number + # The program cannot check for max_approval each time because it + # decreases + # with each trade. + self.max_approval_hex = f"0x{64 * 'f'}" + self.max_approval_int = int(self.max_approval_hex, 16) + self.max_approval_check_hex = f"0x{15 * '0'}{49 * 'f'}" + self.max_approval_check_int = int(self.max_approval_check_hex, 16) + self.gas_limit = max_gas + self.gas_price = max_gprice + self.london_style = london_fork + self.london_priorityfee = max_priorityfee + self.dex_name = dex_name + + chain_id = self.w3.net.version + config = configparser.ConfigParser() + config.read("configs\\evmuniV4_quoter.ini") + quoter_address = config.get("settings",chain_id) + config.read("configs\\evmuniV4_router.ini") + router_address = config.get("settings",chain_id) + config.read("configs\\evmuniV4_stateview.ini") + stateview_address = config.get("settings",chain_id) + config.read("configs\\evmuniV4_permit2.ini") + permit2_address = config.get("settings",chain_id) + + self.quoter_address = _str_to_addr(quoter_address) + self.router_address = _str_to_addr(router_address) + self.stateview_address = _str_to_addr(stateview_address) + self.permit2_address = _str_to_addr(permit2_address) + + self.quoter = _load_contract(self.w3, abi_name = "uniswap-v4/quoter", address = self.quoter_address) + self.router = _load_contract(self.w3, abi_name="uniswap-v4/router", address=self.router_address) + self.stateview = _load_contract(self.w3, abi_name="uniswap-v4/stateview", address=self.stateview_address) + self.permit2 = _load_contract(self.w3, abi_name="uniswap-v4/permit2", address=self.permit2_address) + weth_by_dex[dex_name] = WRAPPED_ETH_ADDRESS + return + + def load_contract_with_abi(self, abi_name: str, address: AddressLike) -> Contract: + return self.w3.eth.contract(address=address, abi=_load_abi(abi_name)) + + def erc20_contract(self, token_addr: AddressLike) -> Contract: + return self.load_contract_with_abi(abi_name="erc20", address=token_addr) + + def approve(self, token: AddressLike, max_approval: Optional[int]=None) -> Any: #<-------------------- FIX ME + """Give an PERMIT2 approval of a token.""" + if(token != ETH_ADDRESS): + max_approval = self.max_approval_int if not max_approval else max_approval + function = self.erc20_contract(token).functions.approve(_addr_to_str(self.permit2_address), max_approval) + print(f"Approving {_addr_to_str(token)} for PERMIT2...") + tx = self._build_and_send_tx(function) + time.sleep(7) + #Give an exchange/router max approval of a token. + max_approval :int = 2 ** 100 - 1 + expiration :int = int(10 ** 12) + print(f"Setting permit for {_addr_to_str(token)} at router contract...") + function = self.permit2.functions.approve(_str_to_addr(token), self.router_address, max_approval, expiration) + tx = self._build_and_send_tx(function) + + return tx + + def approval(self,token: AddressLike): + #[0] current allowance, [1] allowance expiration [2] current nonce + result = int(self.permit2.functions.allowance(self.address, token, self.router.address).call()[0]) + return result + + def _get_tx_params(self, value: int=0 , gas: int=250001) -> dict: + """Get generic transaction parameters.""" + if self.london_style == 0: + return { + "from": _addr_to_str(self.address), + "value": value, + "gas": int(self.gas_limit), + "gasPrice": Web3.to_wei(self.gas_price, 'gwei'), + "nonce": max(self.last_nonce, 0), + } + else: + return { + "from": _addr_to_str(self.address), + "gas": int(self.gas_limit), + "maxPriorityFeePerGas": Web3.to_wei(self.london_priorityfee, 'gwei'), + "maxFeePerGas": Web3.to_wei(self.gas_price, 'gwei'), + "type": 2, + "chainId": self.w3.eth.chain_id, + "value": value, + "nonce": max(self.last_nonce, 0), + } + + + def get_token_token_spot_price(self, token0: str, token1: str, fee: int, tick_spacing: int, hooks: str) -> int: + """Current spot price for token to token trades.""" + if token0 > token1: + (token1, token0) = (token0, token1) + pool_key = eth_abi.abi.encode(types=["address", "address", "uint24", "int24", "address"], + args=[token0, + token1, + fee, + tick_spacing, + hooks,],) + pool_id = Web3.keccak(pool_key) + if self.version == 4: + price : int = self.stateview.functions.getSlot0(pool_id.hex()).call()[0] + else: + raise ValueError("Function not supported for this version of Sunswap") + return price + + def get_quote_exact_input_single(self, token0: AddressLike, token1: AddressLike, qty: int, fee: int=500, tick_spacing: int=10, hooks: AddressLike=ZERO_HOOK, hook_data:bytes=bytes()) -> int: + """Quote for token to token single hop trades with an exact input.""" + if self.version == 4: + if(token0 < token1): + zero_for_one = True + else: + zero_for_one = False + (token1, token0) = (token0, token1) + pool_key = (token0, + token1, + fee, + tick_spacing, + hooks) + #[0] The output quote [1] estimated gas units used for the swap + price : int = self.quoter.functions.quoteExactInputSingle((pool_key, zero_for_one,qty, hook_data)).call()[0] + else: + raise ValueError("Function not supported for this version of Sunswap") + return price + + def get_quote_exact_output_single(self, token0: AddressLike, token1: AddressLike, qty: int, fee: int=500, tick_spacing: int=10, hooks: AddressLike=ZERO_HOOK, hook_data:bytes=bytes()) -> int: + """Quote for token to token single hop trades with an exact output.""" + if self.version == 4: + if(token0 < token1): + zero_for_one = True + else: + zero_for_one = False + (token1, token0) = (token0, token1) + + pool_key = (token0, + token1, + fee, + tick_spacing, + hooks,) + #[0] The input quote [1] estimated gas units used for the swap + price : int = self.quoter.functions.quoteExactOutputSingle((pool_key, zero_for_one,qty, hook_data)).call()[0] + else: + raise ValueError("Function not supported for this version of Sunswap") + return price + + def _token_to_token_swap_input(self, + input_token: str, + qty: int, + qtycap: int, + output_token: str, + recipient: Optional[AddressLike], + fee: int, + tick_spacing: int, + hooks: str,) -> HexBytes: + if self.version == 4: + if recipient is None: + recipient = self.address + + min_tokens_bought = int((1 - self.max_slippage) * qtycap) + + ether_amount = 0 + if(input_token == ETH_ADDRESS): + ether_amount = qty + + #V4_SWAP // Encode swap actions + commands = encode_packed(["uint8"], args=[0x10],) + + #SWAP_EXACT_IN_SINGLE, SETTLE_ALL, TAKE_ALL + actions = encode_packed(["uint8","uint8","uint8"], [0x06, 0x0C, 0x0F],) + + #SETTING PARAMS + pool_key = (input_token, + output_token, + fee, + tick_spacing, + hooks) + if input_token < output_token: + zero_for_one = True + (token0, token1) = (input_token, output_token) + else: + zero_for_one = False + (token0, token1) = (output_token,input_token) + exact_input_single_params = encode(['((address,address,uint24,int24,address),bool,int128,uint128,bytes)'], + [((token0, token1, fee, tick_spacing, hooks), zero_for_one, qty, min_tokens_bought, bytes(0))],) + settle_all_params = encode(['address','uint128'], [input_token, qty],) + take_all_params = encode(['address','uint128'], [output_token, min_tokens_bought],) + + #ENCODING DATA + params = [exact_input_single_params, settle_all_params, take_all_params] + inputs = [] + inputs.append(encode(['bytes','bytes[]'], [actions, params],)) + + return self._build_and_send_tx(self.router.functions.execute(commands, inputs, self._deadline()), self._get_tx_params(value=ether_amount)) + else: + raise ValueError + + + def _token_to_token_swap_output(self, + input_token: AddressLike, + qty: int, + qtycap: int, + output_token: AddressLike, + recipient: Optional[AddressLike], + fee: int, + tick_spacing, + hooks: AddressLike,) -> HexBytes: + if self.version == 4: + if recipient is None: + recipient = self.address + + amount_in_max = int((1 + self.max_slippage) * qtycap) + + ether_amount = 0 + if(input_token == ETH_ADDRESS): + ether_amount = qty + + #V4_SWAP // Encode swap actions + commands = encode_packed(["uint8"], + args=[0x10],) + + #SWAP_EXACT_OUT_SINGLE, SETTLE_ALL, TAKE_ALL + actions = encode_packed(["uint8","uint8","uint8"], + args=[0x09, 0x0C, 0x0F],) + #SETTING PARAMS + pool_key = (input_token, + output_token, + fee, + tick_spacing, + hooks,) + if input_token < output_token: + zero_for_one = True + (token0, token1) = (input_token, output_token) + else: + zero_for_one = False + (token0, token1) = (output_token,input_token) + exact_output_single_params = encode(['((address,address,uint24,int24,address),bool,int128,uint128,bytes)'], + [((token0, token1,fee,tick_spacing,hooks,), zero_for_one, qty, amount_in_max, bytes(0))],) + settle_all_params = encode(["address","uint128"], [input_token, amount_in_max],) + take_all_params = encode(["address","uint128"], [output_token, qty],) + + #ENCODING DATA + params = (exact_output_single_params, settle_all_params, take_all_params) + inputs = [] + inputs.append(encode(["bytes","bytes[]"], [actions, params],)) + + return self._build_and_send_tx(self.router.functions.execute(commands, inputs, self._deadline()), self._get_tx_params(value=ether_amount)) + else: + raise ValueError + + + + def drop_txn(self, + address_to: AddressLike, + gwei: float, + gasv: float, + priorityfee: int=10) -> HexBytes: + + signed_txn = self.w3.eth.account.sign_transaction(dict(chainId=int(self.w3.net.version), + nonce=self.last_nonce, + gasPrice = Web3.to_wei(self.gas_price, 'gwei'), + gas = int(self.gas_limit), + to = Web3.to_checksum_address(address_to), + value = Web3.to_wei(0,'wei')), self.private_key) + signed_txn_london = self.w3.eth.account.sign_transaction(dict(chainId=int(self.w3.net.version), + type=2, + nonce=self.last_nonce, + maxFeePerGas = Web3.to_wei(int(gwei), 'gwei'), + maxPriorityFeePerGas = Web3.to_wei(priorityfee, 'gwei'), + gas = int(21000), + to = Web3.to_checksum_address(address_to), + value = Web3.to_wei(0,'wei')), self.private_key) + if self.london_style == 1: + return self.w3.eth.send_raw_transaction(signed_txn_london.rawTransaction) + else: + return self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) + + def make_swap_input(self, + input_token: AddressLike, + output_token: AddressLike, + qty: int, + qtycap: int, + swap_pool_key: pool_key, + recipient: AddressLike=None, + fee: int=3000) -> HexBytes: + + return self._token_to_token_swap_input(input_token, qty, qtycap, output_token, recipient, swap_pool_key.fee, swap_pool_key.tick_spacing, swap_pool_key.hooks) + + def make_swap_output(self, + input_token: AddressLike, + output_token: AddressLike, + qty: int, + qtycap: int, + swap_pool_key: pool_key, + recipient: AddressLike=None, + fee: int=3000) -> HexBytes: + + return self._token_to_token_swap_output(swap_pool_key.currency0, qty, qtycap, swap_pool_key.currency1, recipient, swap_pool_key.fee, swap_pool_key.tick_spacing, swap_pool_key.hooks) + + def get_weth_address(self) -> AddressLike: + address : str = weth_by_dex[self.dex_name]() + return address + + def get_token_balance(self, erc20: AddressLike) -> Decimal: + + contract = _load_contract(self.w3, abi_name = "erc20", address = erc20) + decimals = contract.functions.decimals().call() + try: + balance = contract.functions.balanceOf(self.address).call() + except: + balance = 0 + balance = Decimal(balance) / (10 ** decimals) + return balance + + def get_balance(self) -> Decimal: + """Get the balance of ETH for your address.""" + try: + balance = self.w3.eth.get_balance(self.address) + except: + balance = 0 + return balance + + def _deadline(self) -> int: + """Get a predefined deadline. 10min by default.""" + return int(time.time()) + 10 * 60 + + def _build_and_send_tx(self, function: ContractFunction, tx_params: Optional[dict]=None) -> HexBytes: + """Build and send a transaction.""" + if not tx_params: + tx_params = self._get_tx_params() + transaction = function.build_transaction(tx_params) + signed_txn = self.w3.eth.account.sign_transaction(transaction, private_key=self.private_key) + # TODO: This needs to get more complicated if we want to support + # replacing a transaction + # FIXME: This does not play nice if transactions are sent from other + # places using the same wallet. + try: + return self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) + finally: + #logger.debug(f"nonce: {tx_params['nonce']}") + self.last_nonce = Nonce(tx_params["nonce"] + 1) + + +def _load_contract(w3: Web3, abi_name: str, address: AddressLike) -> Contract: + address = Web3.to_checksum_address(address) + return w3.eth.contract(address=address, abi=_load_abi(abi_name)) + +def _load_abi(name: str) -> str: + path = f"{os.path.dirname(os.path.abspath(__file__))}/assets/" + with open(os.path.abspath(path + f"{name}.abi")) as f: + abi = json.load(f) + return abi + From 6a113d5f8522c6a2ae3690bbd58872d7fdb03a3e Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:49:39 +0200 Subject: [PATCH 07/18] Add Uniswap4 to module exports --- uniswap/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniswap/__init__.py b/uniswap/__init__.py index f9f37116..4760ea3c 100644 --- a/uniswap/__init__.py +++ b/uniswap/__init__.py @@ -3,4 +3,4 @@ from .uniswap import Uniswap, _str_to_addr from .uniswap4 import Uniswap4 -__all__ = ["Uniswap", "exceptions", "_str_to_addr", "main"] +__all__ = ["Uniswap", "Uniswap4", "exceptions", "_str_to_addr", "main"] From e0823f817a9048020c7ad0059668017a964263c1 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:51:00 +0200 Subject: [PATCH 08/18] Delete uniswap/uniswap-v4 directory --- uniswap/uniswap-v4/permit2.abi | 901 ------------------------------- uniswap/uniswap-v4/quoter.abi | 637 ---------------------- uniswap/uniswap-v4/router.abi | 1 - uniswap/uniswap-v4/stateview.abi | 365 ------------- 4 files changed, 1904 deletions(-) delete mode 100644 uniswap/uniswap-v4/permit2.abi delete mode 100644 uniswap/uniswap-v4/quoter.abi delete mode 100644 uniswap/uniswap-v4/router.abi delete mode 100644 uniswap/uniswap-v4/stateview.abi diff --git a/uniswap/uniswap-v4/permit2.abi b/uniswap/uniswap-v4/permit2.abi deleted file mode 100644 index 2f0281eb..00000000 --- a/uniswap/uniswap-v4/permit2.abi +++ /dev/null @@ -1,901 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "name": "AllowanceExpired", - "type": "error" - }, - { - "inputs": [], - "name": "ExcessiveInvalidation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "maxAmount", - "type": "uint256" - } - ], - "name": "InvalidAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidContractSignature", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidNonce", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidSignature", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidSignatureLength", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidSigner", - "type": "error" - }, - { - "inputs": [], - "name": "LengthMismatch", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "signatureDeadline", - "type": "uint256" - } - ], - "name": "SignatureExpired", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "indexed": false, - "internalType": "uint48", - "name": "expiration", - "type": "uint48" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "Lockdown", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint48", - "name": "newNonce", - "type": "uint48" - }, - { - "indexed": false, - "internalType": "uint48", - "name": "oldNonce", - "type": "uint48" - } - ], - "name": "NonceInvalidation", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "indexed": false, - "internalType": "uint48", - "name": "expiration", - "type": "uint48" - }, - { - "indexed": false, - "internalType": "uint48", - "name": "nonce", - "type": "uint48" - } - ], - "name": "Permit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "word", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "mask", - "type": "uint256" - } - ], - "name": "UnorderedNonceInvalidation", - "type": "event" - }, - { - "inputs": [], - "name": "DOMAIN_SEPARATOR", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "internalType": "uint48", - "name": "expiration", - "type": "uint48" - }, - { - "internalType": "uint48", - "name": "nonce", - "type": "uint48" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "internalType": "uint48", - "name": "expiration", - "type": "uint48" - } - ], - "name": "approve", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint48", - "name": "newNonce", - "type": "uint48" - } - ], - "name": "invalidateNonces", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "wordPos", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "mask", - "type": "uint256" - } - ], - "name": "invalidateUnorderedNonces", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "internalType": "struct IAllowanceTransfer.TokenSpenderPair[]", - "name": "approvals", - "type": "tuple[]" - } - ], - "name": "lockdown", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "nonceBitmap", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "internalType": "uint48", - "name": "expiration", - "type": "uint48" - }, - { - "internalType": "uint48", - "name": "nonce", - "type": "uint48" - } - ], - "internalType": "struct IAllowanceTransfer.PermitDetails[]", - "name": "details", - "type": "tuple[]" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "sigDeadline", - "type": "uint256" - } - ], - "internalType": "struct IAllowanceTransfer.PermitBatch", - "name": "permitBatch", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "permit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "internalType": "uint48", - "name": "expiration", - "type": "uint48" - }, - { - "internalType": "uint48", - "name": "nonce", - "type": "uint48" - } - ], - "internalType": "struct IAllowanceTransfer.PermitDetails", - "name": "details", - "type": "tuple" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "sigDeadline", - "type": "uint256" - } - ], - "internalType": "struct IAllowanceTransfer.PermitSingle", - "name": "permitSingle", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "permit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.TokenPermissions", - "name": "permitted", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.PermitTransferFrom", - "name": "permit", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "requestedAmount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.SignatureTransferDetails", - "name": "transferDetails", - "type": "tuple" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "permitTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.TokenPermissions[]", - "name": "permitted", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", - "name": "permit", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "requestedAmount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", - "name": "transferDetails", - "type": "tuple[]" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "permitTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.TokenPermissions", - "name": "permitted", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.PermitTransferFrom", - "name": "permit", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "requestedAmount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.SignatureTransferDetails", - "name": "transferDetails", - "type": "tuple" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "witness", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "witnessTypeString", - "type": "string" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "permitWitnessTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.TokenPermissions[]", - "name": "permitted", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", - "name": "permit", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "requestedAmount", - "type": "uint256" - } - ], - "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", - "name": "transferDetails", - "type": "tuple[]" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "witness", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "witnessTypeString", - "type": "string" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "name": "permitWitnessTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "internalType": "struct IAllowanceTransfer.AllowanceTransferDetails[]", - "name": "transferDetails", - "type": "tuple[]" - } - ], - "name": "transferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint160", - "name": "amount", - "type": "uint160" - }, - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "transferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file diff --git a/uniswap/uniswap-v4/quoter.abi b/uniswap/uniswap-v4/quoter.abi deleted file mode 100644 index f38100bc..00000000 --- a/uniswap/uniswap-v4/quoter.abi +++ /dev/null @@ -1,637 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract IPoolManager", - "name": "_poolManager", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - } - ], - "name": "NotEnoughLiquidity", - "type": "error" - }, - { - "inputs": [], - "name": "NotPoolManager", - "type": "error" - }, - { - "inputs": [], - "name": "NotSelf", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "QuoteSwap", - "type": "error" - }, - { - "inputs": [], - "name": "UnexpectedCallSuccess", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "revertData", - "type": "bytes" - } - ], - "name": "UnexpectedRevertBytes", - "type": "error" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "Currency", - "name": "exactCurrency", - "type": "address" - }, - { - "components": [ - { - "internalType": "Currency", - "name": "intermediateCurrency", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct PathKey[]", - "name": "path", - "type": "tuple[]" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - } - ], - "internalType": "struct IV4Quoter.QuoteExactParams", - "name": "params", - "type": "tuple" - } - ], - "name": "_quoteExactInput", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "Currency", - "name": "currency0", - "type": "address" - }, - { - "internalType": "Currency", - "name": "currency1", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - } - ], - "internalType": "struct PoolKey", - "name": "poolKey", - "type": "tuple" - }, - { - "internalType": "bool", - "name": "zeroForOne", - "type": "bool" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct IV4Quoter.QuoteExactSingleParams", - "name": "params", - "type": "tuple" - } - ], - "name": "_quoteExactInputSingle", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "Currency", - "name": "exactCurrency", - "type": "address" - }, - { - "components": [ - { - "internalType": "Currency", - "name": "intermediateCurrency", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct PathKey[]", - "name": "path", - "type": "tuple[]" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - } - ], - "internalType": "struct IV4Quoter.QuoteExactParams", - "name": "params", - "type": "tuple" - } - ], - "name": "_quoteExactOutput", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "Currency", - "name": "currency0", - "type": "address" - }, - { - "internalType": "Currency", - "name": "currency1", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - } - ], - "internalType": "struct PoolKey", - "name": "poolKey", - "type": "tuple" - }, - { - "internalType": "bool", - "name": "zeroForOne", - "type": "bool" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct IV4Quoter.QuoteExactSingleParams", - "name": "params", - "type": "tuple" - } - ], - "name": "_quoteExactOutputSingle", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "poolManager", - "outputs": [ - { - "internalType": "contract IPoolManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "Currency", - "name": "exactCurrency", - "type": "address" - }, - { - "components": [ - { - "internalType": "Currency", - "name": "intermediateCurrency", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct PathKey[]", - "name": "path", - "type": "tuple[]" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - } - ], - "internalType": "struct IV4Quoter.QuoteExactParams", - "name": "params", - "type": "tuple" - } - ], - "name": "quoteExactInput", - "outputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasEstimate", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "Currency", - "name": "currency0", - "type": "address" - }, - { - "internalType": "Currency", - "name": "currency1", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - } - ], - "internalType": "struct PoolKey", - "name": "poolKey", - "type": "tuple" - }, - { - "internalType": "bool", - "name": "zeroForOne", - "type": "bool" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct IV4Quoter.QuoteExactSingleParams", - "name": "params", - "type": "tuple" - } - ], - "name": "quoteExactInputSingle", - "outputs": [ - { - "internalType": "uint256", - "name": "amountOut", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasEstimate", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "Currency", - "name": "exactCurrency", - "type": "address" - }, - { - "components": [ - { - "internalType": "Currency", - "name": "intermediateCurrency", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct PathKey[]", - "name": "path", - "type": "tuple[]" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - } - ], - "internalType": "struct IV4Quoter.QuoteExactParams", - "name": "params", - "type": "tuple" - } - ], - "name": "quoteExactOutput", - "outputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasEstimate", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "Currency", - "name": "currency0", - "type": "address" - }, - { - "internalType": "Currency", - "name": "currency1", - "type": "address" - }, - { - "internalType": "uint24", - "name": "fee", - "type": "uint24" - }, - { - "internalType": "int24", - "name": "tickSpacing", - "type": "int24" - }, - { - "internalType": "contract IHooks", - "name": "hooks", - "type": "address" - } - ], - "internalType": "struct PoolKey", - "name": "poolKey", - "type": "tuple" - }, - { - "internalType": "bool", - "name": "zeroForOne", - "type": "bool" - }, - { - "internalType": "uint128", - "name": "exactAmount", - "type": "uint128" - }, - { - "internalType": "bytes", - "name": "hookData", - "type": "bytes" - } - ], - "internalType": "struct IV4Quoter.QuoteExactSingleParams", - "name": "params", - "type": "tuple" - } - ], - "name": "quoteExactOutputSingle", - "outputs": [ - { - "internalType": "uint256", - "name": "amountIn", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasEstimate", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "unlockCallback", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file diff --git a/uniswap/uniswap-v4/router.abi b/uniswap/uniswap-v4/router.abi deleted file mode 100644 index 2474909f..00000000 --- a/uniswap/uniswap-v4/router.abi +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"},{"internalType":"address","name":"v4PoolManager","type":"address"},{"internalType":"address","name":"v3NFTPositionManager","type":"address"},{"internalType":"address","name":"v4PositionManager","type":"address"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotNegative","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotPositive","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[{"internalType":"bytes4","name":"action","type":"bytes4"}],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidEthSender","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotAuthorizedForToken","type":"error"},{"inputs":[],"name":"NotPoolManager","type":"error"},{"inputs":[],"name":"OnlyMintAllowed","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[{"internalType":"uint256","name":"action","type":"uint256"}],"name":"UnsupportedAction","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountOutReceived","type":"uint256"},{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"V4TooLittleReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxAmountInRequested","type":"uint256"},{"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"V4TooMuchRequested","type":"error"},{"inputs":[],"name":"V3_POSITION_MANAGER","outputs":[{"internalType":"contract INonfungiblePositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"V4_POSITION_MANAGER","outputs":[{"internalType":"contract IPositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"unlockCallback","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/uniswap/uniswap-v4/stateview.abi b/uniswap/uniswap-v4/stateview.abi deleted file mode 100644 index 19d58662..00000000 --- a/uniswap/uniswap-v4/stateview.abi +++ /dev/null @@ -1,365 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract IPoolManager", - "name": "_poolManager", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - } - ], - "name": "getFeeGrowthGlobals", - "outputs": [ - { - "internalType": "uint256", - "name": "feeGrowthGlobal0", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthGlobal1", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - } - ], - "name": "getFeeGrowthInside", - "outputs": [ - { - "internalType": "uint256", - "name": "feeGrowthInside0X128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside1X128", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - } - ], - "name": "getLiquidity", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidity", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "positionId", - "type": "bytes32" - } - ], - "name": "getPositionInfo", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidity", - "type": "uint128" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside0LastX128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside1LastX128", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "int24", - "name": "tickLower", - "type": "int24" - }, - { - "internalType": "int24", - "name": "tickUpper", - "type": "int24" - }, - { - "internalType": "bytes32", - "name": "salt", - "type": "bytes32" - } - ], - "name": "getPositionInfo", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidity", - "type": "uint128" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside0LastX128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthInside1LastX128", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "positionId", - "type": "bytes32" - } - ], - "name": "getPositionLiquidity", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidity", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - } - ], - "name": "getSlot0", - "outputs": [ - { - "internalType": "uint160", - "name": "sqrtPriceX96", - "type": "uint160" - }, - { - "internalType": "int24", - "name": "tick", - "type": "int24" - }, - { - "internalType": "uint24", - "name": "protocolFee", - "type": "uint24" - }, - { - "internalType": "uint24", - "name": "lpFee", - "type": "uint24" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "int16", - "name": "tick", - "type": "int16" - } - ], - "name": "getTickBitmap", - "outputs": [ - { - "internalType": "uint256", - "name": "tickBitmap", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "int24", - "name": "tick", - "type": "int24" - } - ], - "name": "getTickFeeGrowthOutside", - "outputs": [ - { - "internalType": "uint256", - "name": "feeGrowthOutside0X128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthOutside1X128", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "int24", - "name": "tick", - "type": "int24" - } - ], - "name": "getTickInfo", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidityGross", - "type": "uint128" - }, - { - "internalType": "int128", - "name": "liquidityNet", - "type": "int128" - }, - { - "internalType": "uint256", - "name": "feeGrowthOutside0X128", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "feeGrowthOutside1X128", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "PoolId", - "name": "poolId", - "type": "bytes32" - }, - { - "internalType": "int24", - "name": "tick", - "type": "int24" - } - ], - "name": "getTickLiquidity", - "outputs": [ - { - "internalType": "uint128", - "name": "liquidityGross", - "type": "uint128" - }, - { - "internalType": "int128", - "name": "liquidityNet", - "type": "int128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "poolManager", - "outputs": [ - { - "internalType": "contract IPoolManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } -] \ No newline at end of file From 441df016dac8df038d5cf81f5dce837ea08c9350 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:51:24 +0200 Subject: [PATCH 09/18] Add files via upload --- uniswap/assets/uniswap-v4/permit2.abi | 901 ++++++++++++++++++++++++ uniswap/assets/uniswap-v4/quoter.abi | 637 +++++++++++++++++ uniswap/assets/uniswap-v4/router.abi | 1 + uniswap/assets/uniswap-v4/stateview.abi | 365 ++++++++++ 4 files changed, 1904 insertions(+) create mode 100644 uniswap/assets/uniswap-v4/permit2.abi create mode 100644 uniswap/assets/uniswap-v4/quoter.abi create mode 100644 uniswap/assets/uniswap-v4/router.abi create mode 100644 uniswap/assets/uniswap-v4/stateview.abi diff --git a/uniswap/assets/uniswap-v4/permit2.abi b/uniswap/assets/uniswap-v4/permit2.abi new file mode 100644 index 00000000..2f0281eb --- /dev/null +++ b/uniswap/assets/uniswap-v4/permit2.abi @@ -0,0 +1,901 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "AllowanceExpired", + "type": "error" + }, + { + "inputs": [], + "name": "ExcessiveInvalidation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxAmount", + "type": "uint256" + } + ], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidContractSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidNonce", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSigner", + "type": "error" + }, + { + "inputs": [], + "name": "LengthMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + } + ], + "name": "SignatureExpired", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "Lockdown", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "newNonce", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "oldNonce", + "type": "uint48" + } + ], + "name": "NonceInvalidation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "name": "Permit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "word", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mask", + "type": "uint256" + } + ], + "name": "UnorderedNonceInvalidation", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint48", + "name": "newNonce", + "type": "uint48" + } + ], + "name": "invalidateNonces", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "wordPos", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mask", + "type": "uint256" + } + ], + "name": "invalidateUnorderedNonces", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "internalType": "struct IAllowanceTransfer.TokenSpenderPair[]", + "name": "approvals", + "type": "tuple[]" + } + ], + "name": "lockdown", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "nonceBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "internalType": "struct IAllowanceTransfer.PermitDetails[]", + "name": "details", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sigDeadline", + "type": "uint256" + } + ], + "internalType": "struct IAllowanceTransfer.PermitBatch", + "name": "permitBatch", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "internalType": "struct IAllowanceTransfer.PermitDetails", + "name": "details", + "type": "tuple" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sigDeadline", + "type": "uint256" + } + ], + "internalType": "struct IAllowanceTransfer.PermitSingle", + "name": "permitSingle", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions", + "name": "permitted", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails", + "name": "transferDetails", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions[]", + "name": "permitted", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions", + "name": "permitted", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails", + "name": "transferDetails", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "witness", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "witnessTypeString", + "type": "string" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitWitnessTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions[]", + "name": "permitted", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "witness", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "witnessTypeString", + "type": "string" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitWitnessTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "internalType": "struct IAllowanceTransfer.AllowanceTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/quoter.abi b/uniswap/assets/uniswap-v4/quoter.abi new file mode 100644 index 00000000..f38100bc --- /dev/null +++ b/uniswap/assets/uniswap-v4/quoter.abi @@ -0,0 +1,637 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPoolManager", + "name": "_poolManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "NotEnoughLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "NotPoolManager", + "type": "error" + }, + { + "inputs": [], + "name": "NotSelf", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "QuoteSwap", + "type": "error" + }, + { + "inputs": [], + "name": "UnexpectedCallSuccess", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "revertData", + "type": "bytes" + } + ], + "name": "UnexpectedRevertBytes", + "type": "error" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactInput", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactInputSingle", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactOutput", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactOutputSingle", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "poolManager", + "outputs": [ + { + "internalType": "contract IPoolManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactInput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactInputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactOutputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlockCallback", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/router.abi b/uniswap/assets/uniswap-v4/router.abi new file mode 100644 index 00000000..2474909f --- /dev/null +++ b/uniswap/assets/uniswap-v4/router.abi @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"},{"internalType":"address","name":"v4PoolManager","type":"address"},{"internalType":"address","name":"v3NFTPositionManager","type":"address"},{"internalType":"address","name":"v4PositionManager","type":"address"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotNegative","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotPositive","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[{"internalType":"bytes4","name":"action","type":"bytes4"}],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidEthSender","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotAuthorizedForToken","type":"error"},{"inputs":[],"name":"NotPoolManager","type":"error"},{"inputs":[],"name":"OnlyMintAllowed","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[{"internalType":"uint256","name":"action","type":"uint256"}],"name":"UnsupportedAction","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountOutReceived","type":"uint256"},{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"V4TooLittleReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxAmountInRequested","type":"uint256"},{"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"V4TooMuchRequested","type":"error"},{"inputs":[],"name":"V3_POSITION_MANAGER","outputs":[{"internalType":"contract INonfungiblePositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"V4_POSITION_MANAGER","outputs":[{"internalType":"contract IPositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"unlockCallback","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/stateview.abi b/uniswap/assets/uniswap-v4/stateview.abi new file mode 100644 index 00000000..19d58662 --- /dev/null +++ b/uniswap/assets/uniswap-v4/stateview.abi @@ -0,0 +1,365 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPoolManager", + "name": "_poolManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getFeeGrowthGlobals", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthGlobal0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthGlobal1", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "getFeeGrowthInside", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthInside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "positionId", + "type": "bytes32" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "positionId", + "type": "bytes32" + } + ], + "name": "getPositionLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getSlot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "lpFee", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int16", + "name": "tick", + "type": "int16" + } + ], + "name": "getTickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "tickBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickFeeGrowthOutside", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolManager", + "outputs": [ + { + "internalType": "contract IPoolManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file From 019704aa2e47a1bf37757e10b69eabdec9c5aff1 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Sun, 4 Jan 2026 18:19:47 +0200 Subject: [PATCH 10/18] Refactor uniswap4.py by removing unused imports Removed unused imports and cleaned up code formatting. --- uniswap/uniswap4.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/uniswap/uniswap4.py b/uniswap/uniswap4.py index 2b5faab2..dc26fdfc 100644 --- a/uniswap/uniswap4.py +++ b/uniswap/uniswap4.py @@ -12,8 +12,6 @@ from uni4base import * import logging from decimal import Decimal -##from colorama import Fore -##from colorama import Style from typing import List, Any, Optional, Callable, Union, Tuple, Dict from web3.types import ( TxParams, @@ -34,15 +32,6 @@ with open(os.path.abspath(f"assets\\erc20.abi")) as f: erc20_ABI : str = json.load(f) -eth_by_dex = { - "UNISWAPV4" : ETH_ADDRESS, -} -transfer_by_dex = { - "UNISWAPV4" : WRAPPED_ETH_ADDRESS, -} -weth_by_dex = { - "UNISWAPV4" : WRAPPED_ETH_ADDRESS, -} def _addr_to_str(a: AddressLike) -> str: if isinstance(a, bytes): @@ -138,7 +127,6 @@ def __init__(self, self.router = _load_contract(self.w3, abi_name="uniswap-v4/router", address=self.router_address) self.stateview = _load_contract(self.w3, abi_name="uniswap-v4/stateview", address=self.stateview_address) self.permit2 = _load_contract(self.w3, abi_name="uniswap-v4/permit2", address=self.permit2_address) - weth_by_dex[dex_name] = WRAPPED_ETH_ADDRESS return def load_contract_with_abi(self, abi_name: str, address: AddressLike) -> Contract: @@ -401,10 +389,6 @@ def make_swap_output(self, return self._token_to_token_swap_output(swap_pool_key.currency0, qty, qtycap, swap_pool_key.currency1, recipient, swap_pool_key.fee, swap_pool_key.tick_spacing, swap_pool_key.hooks) - def get_weth_address(self) -> AddressLike: - address : str = weth_by_dex[self.dex_name]() - return address - def get_token_balance(self, erc20: AddressLike) -> Decimal: contract = _load_contract(self.w3, abi_name = "erc20", address = erc20) @@ -455,3 +439,4 @@ def _load_abi(name: str) -> str: abi = json.load(f) return abi + From 74e72b43214c2b2ff86ef3baef3cb9ad0e3a035d Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:35:29 +0200 Subject: [PATCH 11/18] Add files via upload Pool manager contract ABI --- uniswap/assets/uniswap-v4/poolmanager.abi | 1366 +++++++++++++++++++++ 1 file changed, 1366 insertions(+) create mode 100644 uniswap/assets/uniswap-v4/poolmanager.abi diff --git a/uniswap/assets/uniswap-v4/poolmanager.abi b/uniswap/assets/uniswap-v4/poolmanager.abi new file mode 100644 index 00000000..a4ef1f84 --- /dev/null +++ b/uniswap/assets/uniswap-v4/poolmanager.abi @@ -0,0 +1,1366 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyUnlocked", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "currency0", + "type": "address" + }, + { + "internalType": "address", + "name": "currency1", + "type": "address" + } + ], + "name": "CurrenciesOutOfOrderOrEqual", + "type": "error" + }, + { + "inputs": [], + "name": "CurrencyNotSettled", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateCallNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCaller", + "type": "error" + }, + { + "inputs": [], + "name": "ManagerLocked", + "type": "error" + }, + { + "inputs": [], + "name": "MustClearExactPositiveDelta", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroNativeValue", + "type": "error" + }, + { + "inputs": [], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolFeeCurrencySynced", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "ProtocolFeeTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "SwapAmountCannotBeZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "TickSpacingTooLarge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "TickSpacingTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedDynamicLPFeeUpdate", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Donate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "int256", + "name": "liquidityDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "ModifyLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "OperatorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "protocolFeeController", + "type": "address" + } + ], + "name": "ProtocolFeeControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + } + ], + "name": "ProtocolFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "int128", + "name": "amount0", + "type": "int128" + }, + { + "indexed": false, + "internalType": "int128", + "name": "amount1", + "type": "int128" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "clear", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "collectProtocolFees", + "outputs": [ + { + "internalType": "uint256", + "name": "amountCollected", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "donate", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "delta", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "startSlot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "nSlots", + "type": "uint256" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "slots", + "type": "bytes32[]" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "slots", + "type": "bytes32[]" + } + ], + "name": "exttload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "exttload", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [ + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isOperator", + "outputs": [ + { + "internalType": "bool", + "name": "isOperator", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "int256", + "name": "liquidityDelta", + "type": "int256" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "internalType": "struct IPoolManager.ModifyLiquidityParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "modifyLiquidity", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "callerDelta", + "type": "int256" + }, + { + "internalType": "BalanceDelta", + "name": "feesAccrued", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFeeController", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + } + ], + "name": "protocolFeesAccrued", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setOperator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint24", + "name": "newProtocolFee", + "type": "uint24" + } + ], + "name": "setProtocolFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "setProtocolFeeController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "settle", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "settleFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + } + ], + "internalType": "struct IPoolManager.SwapParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "swapDelta", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + } + ], + "name": "sync", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "take", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlock", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint24", + "name": "newDynamicLPFee", + "type": "uint24" + } + ], + "name": "updateDynamicLPFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file From e33e9d24a7a45cbb17a07bee8cd630572eaab8c6 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:37:38 +0200 Subject: [PATCH 12/18] Delete uniswap/configs/evmuniV4_permit2.ini Deleted due to naming fix --- uniswap/configs/evmuniV4_permit2.ini | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 uniswap/configs/evmuniV4_permit2.ini diff --git a/uniswap/configs/evmuniV4_permit2.ini b/uniswap/configs/evmuniV4_permit2.ini deleted file mode 100644 index c6ee0ede..00000000 --- a/uniswap/configs/evmuniV4_permit2.ini +++ /dev/null @@ -1,16 +0,0 @@ -#Format: -#chainID=router contract address -[settings] -1=0x000000000022D473030F116dDEE9F6B43aC78BA3 -130=0x000000000022D473030F116dDEE9F6B43aC78BA3 -10=0x000000000022D473030F116dDEE9F6B43aC78BA3 -8453=0x000000000022D473030F116dDEE9F6B43aC78BA3 -42161=0x000000000022D473030F116dDEE9F6B43aC78BA3 -137=0x000000000022D473030F116dDEE9F6B43aC78BA3 -81457=0x000000000022D473030F116dDEE9F6B43aC78BA3 -7777777=0x000000000022D473030F116dDEE9F6B43aC78BA3 -480=0x000000000022D473030F116dDEE9F6B43aC78BA3 -57073=0x000000000022D473030F116dDEE9F6B43aC78BA3 -1868=0x000000000022D473030F116dDEE9F6B43aC78BA3 -43114=0x000000000022D473030F116dDEE9F6B43aC78BA3 -56=0x000000000022D473030F116dDEE9F6B43aC78BA3 From f7d82749662d0b01ad1800cc9d9c94222bf19476 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:38:01 +0200 Subject: [PATCH 13/18] Delete uniswap/configs/evmuniV4_chains.ini Deleted due to naming fix --- uniswap/configs/evmuniV4_chains.ini | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 uniswap/configs/evmuniV4_chains.ini diff --git a/uniswap/configs/evmuniV4_chains.ini b/uniswap/configs/evmuniV4_chains.ini deleted file mode 100644 index 4739b909..00000000 --- a/uniswap/configs/evmuniV4_chains.ini +++ /dev/null @@ -1,16 +0,0 @@ -#Format: -#chainID=chain name -[settings] -1=MAINNET -130=Unichain -10=Optimism -8453=Base -42161=Arbitrum -137=Polygon -81457=Blast -7777777=Zora -480=Worldchain -57073=Ink -1868=Soneium -43114=Avalanche -56=BNB From d7387a2e1c96cd3f497665ea0eaaffd3e8c9eded Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:38:17 +0200 Subject: [PATCH 14/18] Delete uniswap/configs/evmuniV4_quoter.ini Deleted due to naming fix --- uniswap/configs/evmuniV4_quoter.ini | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 uniswap/configs/evmuniV4_quoter.ini diff --git a/uniswap/configs/evmuniV4_quoter.ini b/uniswap/configs/evmuniV4_quoter.ini deleted file mode 100644 index f6f27dd8..00000000 --- a/uniswap/configs/evmuniV4_quoter.ini +++ /dev/null @@ -1,16 +0,0 @@ -#Format: -#chainID=quoter contract address -[settings] -1=0x52f0e24d1c21c8a0cb1e5a5dd6198556bd9e1203 -130=0x333e3c607b141b18ff6de9f258db6e77fe7491e0 -10=0x1f3131a13296fb91c90870043742c3cdbff1a8d7 -8453=0x0d5e0f971ed27fbff6c2837bf31316121532048d -42161=0x3972c00f7ed4885e145823eb7c655375d275a1c5 -137=0xb3d5c3dfc3a7aebff71895a7191796bffc2c81b9 -81457=0x6f71cdcb0d119ff72c6eb501abceb576fbf62bcf -7777777=0x5edaccc0660e0a2c44b06e07ce8b915e625dc2c6 -480=0x55d235b3ff2daf7c3ede0defc9521f1d6fe6c5c0 -57073=0x3972c00f7ed4885e145823eb7c655375d275a1c5 -1868=0x3972c00f7ed4885e145823eb7c655375d275a1c5 -43114=0xbe40675bb704506a3c2ccfb762dcfd1e979845c2 -56=0x9f75dd27d6664c475b90e105573e550ff69437b0 \ No newline at end of file From b21f2ee45922a70df78f548a26dbaad8c9ce1876 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:38:34 +0200 Subject: [PATCH 15/18] Delete uniswap/configs/evmuniV4_router.ini Deleted due to naming fix --- uniswap/configs/evmuniV4_router.ini | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 uniswap/configs/evmuniV4_router.ini diff --git a/uniswap/configs/evmuniV4_router.ini b/uniswap/configs/evmuniV4_router.ini deleted file mode 100644 index 0cb1f7dd..00000000 --- a/uniswap/configs/evmuniV4_router.ini +++ /dev/null @@ -1,16 +0,0 @@ -#Format: -#chainID=router contract address -[settings] -1=0x66a9893cc07d91d95644aedd05d03f95e1dba8af -130=0xef740bf23acae26f6492b10de645d6b98dc8eaf3 -10=0x851116d9223fabed8e56c0e6b8ad0c31d98b3507 -8453=0x6ff5693b99212da76ad316178a184ab56d299b43 -42161=0xa51afafe0263b40edaef0df8781ea9aa03e381a3 -137=0x1095692a6237d83c6a72f3f5efedb9a670c49223 -81457=0xeabbcb3e8e415306207ef514f660a3f820025be3 -7777777=0x3315ef7ca28db74abadc6c44570efdf06b04b020 -480=0x8ac7bee993bb44dab564ea4bc9ea67bf9eb5e743 -57073=0x112908dac86e20e7241b0927479ea3bf935d1fa0 -1868=0x4cded7edf52c8aa5259a54ec6a3ce7c6d2a455df -43114=0x94b75331ae8d42c1b61065089b7d48fe14aa73b7 -56=0x1906c1d672b88cd1b9ac7593301ca990f94eae07 From e097c4cdec7a7b95016958a7575836344527be20 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:38:48 +0200 Subject: [PATCH 16/18] Delete uniswap/configs/evmuniV4_stateview.ini Deleted due to naming fix --- uniswap/configs/evmuniV4_stateview.ini | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 uniswap/configs/evmuniV4_stateview.ini diff --git a/uniswap/configs/evmuniV4_stateview.ini b/uniswap/configs/evmuniV4_stateview.ini deleted file mode 100644 index 7bb9d6be..00000000 --- a/uniswap/configs/evmuniV4_stateview.ini +++ /dev/null @@ -1,16 +0,0 @@ -#Format: -#chainID=router contract address -[settings] -1=0x7ffe42c4a5deea5b0fec41c94c136cf115597227 -130=0x86e8631a016f9068c3f085faf484ee3f5fdee8f2 -10=0xc18a3169788f4f75a170290584eca6395c75ecdb -8453=0xa3c0c9b65bad0b08107aa264b0f3db444b867a71 -42161=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 -137=0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a -81457=0x12a88ae16f46dce4e8b15368008ab3380885df30 -7777777=0x385785af07d63b50d0a0ea57c4ff89d06adf7328 -480=0x51d394718bc09297262e368c1a481217fdeb71eb -57073=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 -1868=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 -43114=0xc3c9e198c735a4b97e3e683f391ccbdd60b69286 -56=0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4 From 3a55c6a65957b2397b8d7fd5a0e38bc57047a79a Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:39:52 +0200 Subject: [PATCH 17/18] Add files via upload Naming fix --- uniswap/configs/chains.ini | 16 ++++++++++++++++ uniswap/configs/permit2.ini | 16 ++++++++++++++++ uniswap/configs/poolmanager.ini | 16 ++++++++++++++++ uniswap/configs/quoter.ini | 16 ++++++++++++++++ uniswap/configs/router.ini | 16 ++++++++++++++++ uniswap/configs/stateview.ini | 16 ++++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 uniswap/configs/chains.ini create mode 100644 uniswap/configs/permit2.ini create mode 100644 uniswap/configs/poolmanager.ini create mode 100644 uniswap/configs/quoter.ini create mode 100644 uniswap/configs/router.ini create mode 100644 uniswap/configs/stateview.ini diff --git a/uniswap/configs/chains.ini b/uniswap/configs/chains.ini new file mode 100644 index 00000000..4739b909 --- /dev/null +++ b/uniswap/configs/chains.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=chain name +[settings] +1=MAINNET +130=Unichain +10=Optimism +8453=Base +42161=Arbitrum +137=Polygon +81457=Blast +7777777=Zora +480=Worldchain +57073=Ink +1868=Soneium +43114=Avalanche +56=BNB diff --git a/uniswap/configs/permit2.ini b/uniswap/configs/permit2.ini new file mode 100644 index 00000000..c6ee0ede --- /dev/null +++ b/uniswap/configs/permit2.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x000000000022D473030F116dDEE9F6B43aC78BA3 +130=0x000000000022D473030F116dDEE9F6B43aC78BA3 +10=0x000000000022D473030F116dDEE9F6B43aC78BA3 +8453=0x000000000022D473030F116dDEE9F6B43aC78BA3 +42161=0x000000000022D473030F116dDEE9F6B43aC78BA3 +137=0x000000000022D473030F116dDEE9F6B43aC78BA3 +81457=0x000000000022D473030F116dDEE9F6B43aC78BA3 +7777777=0x000000000022D473030F116dDEE9F6B43aC78BA3 +480=0x000000000022D473030F116dDEE9F6B43aC78BA3 +57073=0x000000000022D473030F116dDEE9F6B43aC78BA3 +1868=0x000000000022D473030F116dDEE9F6B43aC78BA3 +43114=0x000000000022D473030F116dDEE9F6B43aC78BA3 +56=0x000000000022D473030F116dDEE9F6B43aC78BA3 diff --git a/uniswap/configs/poolmanager.ini b/uniswap/configs/poolmanager.ini new file mode 100644 index 00000000..96799a89 --- /dev/null +++ b/uniswap/configs/poolmanager.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=pool manager contract address +[settings] +1=0x000000000004444c5dc75cB358380D2e3dE08A90 +130=0x1f98400000000000000000000000000000000004 +10=0x9a13f98cb987694c9f086b1f5eb990eea8264ec3 +8453=0x498581ff718922c3f8e6a244956af099b2652b2b +42161=0x360e68faccca8ca495c1b759fd9eee466db9fb32 +137=0x67366782805870060151383f4bbff9dab53e5cd6 +81457=0x1631559198a9e474033433b2958dabc135ab6446 +7777777=0x0575338e4c17006ae181b47900a84404247ca30f +480=0xb1860d529182ac3bc1f51fa2abd56662b7d13f33 +57073=0x360e68faccca8ca495c1b759fd9eee466db9fb32 +1868=0x360e68faccca8ca495c1b759fd9eee466db9fb32 +43114=0x06380c0e0912312b5150364b9dc4542ba0dbbc85 +56= 0x28e2ea090877bf75740558f6bfb36a5ffee9e9df \ No newline at end of file diff --git a/uniswap/configs/quoter.ini b/uniswap/configs/quoter.ini new file mode 100644 index 00000000..f6f27dd8 --- /dev/null +++ b/uniswap/configs/quoter.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=quoter contract address +[settings] +1=0x52f0e24d1c21c8a0cb1e5a5dd6198556bd9e1203 +130=0x333e3c607b141b18ff6de9f258db6e77fe7491e0 +10=0x1f3131a13296fb91c90870043742c3cdbff1a8d7 +8453=0x0d5e0f971ed27fbff6c2837bf31316121532048d +42161=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +137=0xb3d5c3dfc3a7aebff71895a7191796bffc2c81b9 +81457=0x6f71cdcb0d119ff72c6eb501abceb576fbf62bcf +7777777=0x5edaccc0660e0a2c44b06e07ce8b915e625dc2c6 +480=0x55d235b3ff2daf7c3ede0defc9521f1d6fe6c5c0 +57073=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +1868=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +43114=0xbe40675bb704506a3c2ccfb762dcfd1e979845c2 +56=0x9f75dd27d6664c475b90e105573e550ff69437b0 \ No newline at end of file diff --git a/uniswap/configs/router.ini b/uniswap/configs/router.ini new file mode 100644 index 00000000..0cb1f7dd --- /dev/null +++ b/uniswap/configs/router.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x66a9893cc07d91d95644aedd05d03f95e1dba8af +130=0xef740bf23acae26f6492b10de645d6b98dc8eaf3 +10=0x851116d9223fabed8e56c0e6b8ad0c31d98b3507 +8453=0x6ff5693b99212da76ad316178a184ab56d299b43 +42161=0xa51afafe0263b40edaef0df8781ea9aa03e381a3 +137=0x1095692a6237d83c6a72f3f5efedb9a670c49223 +81457=0xeabbcb3e8e415306207ef514f660a3f820025be3 +7777777=0x3315ef7ca28db74abadc6c44570efdf06b04b020 +480=0x8ac7bee993bb44dab564ea4bc9ea67bf9eb5e743 +57073=0x112908dac86e20e7241b0927479ea3bf935d1fa0 +1868=0x4cded7edf52c8aa5259a54ec6a3ce7c6d2a455df +43114=0x94b75331ae8d42c1b61065089b7d48fe14aa73b7 +56=0x1906c1d672b88cd1b9ac7593301ca990f94eae07 diff --git a/uniswap/configs/stateview.ini b/uniswap/configs/stateview.ini new file mode 100644 index 00000000..7bb9d6be --- /dev/null +++ b/uniswap/configs/stateview.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x7ffe42c4a5deea5b0fec41c94c136cf115597227 +130=0x86e8631a016f9068c3f085faf484ee3f5fdee8f2 +10=0xc18a3169788f4f75a170290584eca6395c75ecdb +8453=0xa3c0c9b65bad0b08107aa264b0f3db444b867a71 +42161=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +137=0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a +81457=0x12a88ae16f46dce4e8b15368008ab3380885df30 +7777777=0x385785af07d63b50d0a0ea57c4ff89d06adf7328 +480=0x51d394718bc09297262e368c1a481217fdeb71eb +57073=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +1868=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +43114=0xc3c9e198c735a4b97e3e683f391ccbdd60b69286 +56=0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4 From 60a08b8e358891c0c78f469bc7453ea8aa992925 Mon Sep 17 00:00:00 2001 From: Yohan K <72107640+liquid-8@users.noreply.github.com> Date: Thu, 15 Jan 2026 07:42:52 +0200 Subject: [PATCH 18/18] Add files via upload Liquidity pools list management class added, UniswapV4 improvements. --- uniswap/__init__.py | 3 +- uniswap/uni4base.py | 29 ----- uniswap/uniswap4.py | 294 ++++++++++++++++++++++++++++++++------------ uniswap/v4pools.py | 137 +++++++++++++++++++++ 4 files changed, 353 insertions(+), 110 deletions(-) create mode 100644 uniswap/v4pools.py diff --git a/uniswap/__init__.py b/uniswap/__init__.py index 4760ea3c..1f0e8ade 100644 --- a/uniswap/__init__.py +++ b/uniswap/__init__.py @@ -2,5 +2,6 @@ from .cli import main from .uniswap import Uniswap, _str_to_addr from .uniswap4 import Uniswap4 +from .v4pools import V4pools -__all__ = ["Uniswap", "Uniswap4", "exceptions", "_str_to_addr", "main"] +__all__ = ["Uniswap", "Uniswap4", "V4pools", "exceptions", "_str_to_addr", "main"] diff --git a/uniswap/uni4base.py b/uniswap/uni4base.py index 615a3e22..da89a37c 100644 --- a/uniswap/uni4base.py +++ b/uniswap/uni4base.py @@ -1,22 +1,3 @@ -import os -import json -import time -import logging -import functools -from typing import List, Any, Optional, Callable, Union, Tuple, Dict - -from web3 import Web3 -from web3.types import ( - TxParams, - Wei, - Address, - ChecksumAddress, - ENS, - Nonce, - HexBytes, -) -from eth_utils import is_same_address -from eth_typing import AnyAddress ZERO_HOOK = "0x0000000000000000000000000000000000000000" ETH_ADDRESS = "0x0000000000000000000000000000000000000000" @@ -30,13 +11,3 @@ class pool_key: tick_spacing : int hooks : str - -class InvalidToken(Exception): - def __init__(self, address: Any) -> None: - Exception.__init__(self, f"Invalid token address: {address}") - - -class InsufficientBalance(Exception): - def __init__(self, had: int, needed: int) -> None: - Exception.__init__(self, f"Insufficient balance. Had {had}, needed {needed}") - diff --git a/uniswap/uniswap4.py b/uniswap/uniswap4.py index dc26fdfc..c5715d4c 100644 --- a/uniswap/uniswap4.py +++ b/uniswap/uniswap4.py @@ -9,7 +9,6 @@ import os import fnmatch import configparser -from uni4base import * import logging from decimal import Decimal from typing import List, Any, Optional, Callable, Union, Tuple, Dict @@ -24,44 +23,29 @@ ) import json import ctypes +from .uni4base import * +from .token import ERC20Token +from .types import AddressLike +from .util import ( + _addr_to_str, + _load_contract, + _load_contract_erc20, + _load_abi, + _str_to_addr, + _validate_address, + chunks, + encode_sqrt_ratioX96, + is_same_address, + nearest_tick, + realised_fee_percentage, +) - -AddressLike = Union[Address, ChecksumAddress, ENS] _netid_to_name = {1000: "mainnet", 1001: "nile"} with open(os.path.abspath(f"assets\\erc20.abi")) as f: erc20_ABI : str = json.load(f) -def _addr_to_str(a: AddressLike) -> str: - if isinstance(a, bytes): - # Address or ChecksumAddress - addr : str = Web3.to_checksum_address("0x" + bytes(a).hex()) - return addr - elif isinstance(a, str): - if a.endswith(".ens"): - # Address is ENS - raise Exception("ENS not supported for this operation") - elif a.startswith("0x"): - addr = Web3.to_checksum_address(a) - return addr - else: - raise InvalidToken(a) - -def _str_to_addr(s: str) -> AddressLike: - if s.startswith("0x"): - return Address(bytes.fromhex(s[2:])) - elif s.endswith(".ens"): - return ENS(s) - else: - raise Exception("Could't convert string {s} to AddressLike") - -def is_version3(dex: str) -> bool: - if "V3" in dex: - return True - else: - return False - class Uniswap4(): def __init__(self, address: Union[str, AddressLike], @@ -70,11 +54,10 @@ def __init__(self, web3: Web3=None, version: int=4, max_slippage: float=0.1, - max_gas: float=250001.0, - max_gprice: float=18.0, - dex_name: str="", + max_gas: float=250000.0, + max_gprice: float=1.80, london_fork: int=1, - max_priorityfee: float=2.0,) -> None: + max_priorityfee: float=1.0,) -> None: self.address : AddressLike = _str_to_addr(address) if isinstance(address, str) else address self.private_key = private_key @@ -105,17 +88,16 @@ def __init__(self, self.gas_price = max_gprice self.london_style = london_fork self.london_priorityfee = max_priorityfee - self.dex_name = dex_name chain_id = self.w3.net.version config = configparser.ConfigParser() - config.read("configs\\evmuniV4_quoter.ini") + config.read("configs\\quoter.ini") quoter_address = config.get("settings",chain_id) - config.read("configs\\evmuniV4_router.ini") + config.read("configs\\router.ini") router_address = config.get("settings",chain_id) - config.read("configs\\evmuniV4_stateview.ini") + config.read("configs\\stateview.ini") stateview_address = config.get("settings",chain_id) - config.read("configs\\evmuniV4_permit2.ini") + config.read("configs\\permit2.ini") permit2_address = config.get("settings",chain_id) self.quoter_address = _str_to_addr(quoter_address) @@ -129,13 +111,18 @@ def __init__(self, self.permit2 = _load_contract(self.w3, abi_name="uniswap-v4/permit2", address=self.permit2_address) return - def load_contract_with_abi(self, abi_name: str, address: AddressLike) -> Contract: + def load_contract_with_abi(self, + abi_name: str, + address: AddressLike) -> Contract: return self.w3.eth.contract(address=address, abi=_load_abi(abi_name)) - def erc20_contract(self, token_addr: AddressLike) -> Contract: + def erc20_contract(self, + token_addr: AddressLike) -> Contract: return self.load_contract_with_abi(abi_name="erc20", address=token_addr) - def approve(self, token: AddressLike, max_approval: Optional[int]=None) -> Any: #<-------------------- FIX ME + def approve(self, + token: AddressLike, + max_approval: Optional[int]=None) -> HexBytes: """Give an PERMIT2 approval of a token.""" if(token != ETH_ADDRESS): max_approval = self.max_approval_int if not max_approval else max_approval @@ -152,12 +139,15 @@ def approve(self, token: AddressLike, max_approval: Optional[int]=None) -> Any: return tx - def approval(self,token: AddressLike): - #[0] current allowance, [1] allowance expiration [2] current nonce + def approval(self, + token: AddressLike): + #[0]=current allowance, [1]=allowance expiration [2]=current nonce result = int(self.permit2.functions.allowance(self.address, token, self.router.address).call()[0]) return result - def _get_tx_params(self, value: int=0 , gas: int=250001) -> dict: + def _get_tx_params(self, + value: int=0 , + gas: int=250000) -> dict: """Get generic transaction parameters.""" if self.london_style == 0: return { @@ -178,26 +168,61 @@ def _get_tx_params(self, value: int=0 , gas: int=250001) -> dict: "value": value, "nonce": max(self.last_nonce, 0), } - - - def get_token_token_spot_price(self, token0: str, token1: str, fee: int, tick_spacing: int, hooks: str) -> int: + #Gas customization + #Gas limit + def get_gas_limit(self) -> float: + return self.gas_limit + + def set_gas_limit(self, gas_limit: float): + self.gas_limit = gas_limit + + #Gas price in GWei + def get_gas_price(self) -> float: + return self.gas_price + + def set_gas_price(self, gas_price: float): + self.gas_price = gas_price + + #Priority fee in GWei + def get_gas_priorityfee(self) -> float: + return self.london_priorityfee + + def set_gas_priorityfee(self, gas_priorityfee: float): + self.london_priorityfee = gas_priorityfee + + #Tokens price functions + def get_token_token_spot_price(self, + token0: AddressLike, + token1: AddressLike, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK,) -> int: """Current spot price for token to token trades.""" if token0 > token1: (token1, token0) = (token0, token1) - pool_key = eth_abi.abi.encode(types=["address", "address", "uint24", "int24", "address"], - args=[token0, - token1, - fee, - tick_spacing, - hooks,],) - pool_id = Web3.keccak(pool_key) + + pool = pool_key() + pool.currency0 = token0 + pool.currency1 = token1 + pool.fee = fee + pool.tick_spacing = tick_spacing + pool.hooks = hooks + pool_id = self.get_pool_id(pool) + if self.version == 4: price : int = self.stateview.functions.getSlot0(pool_id.hex()).call()[0] else: - raise ValueError("Function not supported for this version of Sunswap") + raise ValueError("Function is not supported for this version") return price - def get_quote_exact_input_single(self, token0: AddressLike, token1: AddressLike, qty: int, fee: int=500, tick_spacing: int=10, hooks: AddressLike=ZERO_HOOK, hook_data:bytes=bytes()) -> int: + def get_quote_exact_input_single(self, + token0: AddressLike, + token1: AddressLike, + qty: int, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK, + hook_data:bytes=bytes()) -> int: """Quote for token to token single hop trades with an exact input.""" if self.version == 4: if(token0 < token1): @@ -210,13 +235,119 @@ def get_quote_exact_input_single(self, token0: AddressLike, token1: AddressLike, fee, tick_spacing, hooks) - #[0] The output quote [1] estimated gas units used for the swap + #[0]=The output quote [1]=estimated gas units used for the swap price : int = self.quoter.functions.quoteExactInputSingle((pool_key, zero_for_one,qty, hook_data)).call()[0] else: - raise ValueError("Function not supported for this version of Sunswap") + raise ValueError("Function is not supported for this version") return price - def get_quote_exact_output_single(self, token0: AddressLike, token1: AddressLike, qty: int, fee: int=500, tick_spacing: int=10, hooks: AddressLike=ZERO_HOOK, hook_data:bytes=bytes()) -> int: + def get_pool_id(self, pool: pool_key): + pool_data = eth_abi.abi.encode(types=["address", "address", "uint24", "int24", "address"], + args=[pool.currency0, + pool.currency1, + pool.fee, + pool.tick_spacing, + pool.hooks,],) + pool_id = Web3.keccak(pool_data) + return pool_id + + + def get_token(self, + address: AddressLike, + abi_name: str="erc20") -> ERC20Token: + """ + Retrieves metadata from the ERC20 contract of a given token, like its name, symbol, and decimals. + """ + # FIXME: This function should always return the same output for the + # same input + # and would therefore benefit from caching + if address == "0x0000000000000000000000000000000000000000": + # This isn't exactly right, but for all intents and purposes, + # ETH is treated as a ERC20 by Uniswap. + return ERC20Token(address=address, + name="ETH", + symbol="ETH", + decimals=18,) + token_contract = _load_contract(self.w3, abi_name, address=address) + try: + _name = token_contract.functions.name().call() + _symbol = token_contract.functions.symbol().call() + decimals = token_contract.functions.decimals().call() + except Exception as e: + logger.warning(f"Exception occurred while trying to get token {_addr_to_str(address)}: {e}") + raise InvalidToken(address) + try: + name = _name.decode() + except Exception: + name = _name + try: + symbol = _symbol.decode() + except Exception: + symbol = _symbol + return ERC20Token(symbol, address, name, decimals) + + #Estimates slippage for the given amount of token0 + def estimate_price_impact(self, + token0: AddressLike, + token1: AddressLike, + qty: int, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK, + hook_data:bytes=bytes(), + route: Optional[List[AddressLike]]=None,) -> float: + """ + Returns the estimated price impact as a positive float (0.01 = 1%). + + NOTE: Work-in-progress. + + See ``examples/price_impact.py`` for an example which uses this. + """ + + try: + spot_price = self.get_token_token_spot_price(token0, + token1, + fee, + tick_spacing, + hooks) + except (ArithmeticError, BadFunctionCallOutput): + # ArithmeticError is raised when `token0` amount in the pool + # equals 0. + # BadFunctionCallOutput is raised when the pool for + # given `(token0, token1, fee)` doesn't exist + return 1 + + if spot_price == 0: + # Occurs when `token1` amount in the pool equals 0 + return 1 + try: + quote_amount = self.get_quote_exact_input_single(token0, + token1, + fee, + tick_spacing, + hooks, + hook_data) + except ContractLogicError: + # ContractLogicError is raised when the pool's contract for given + # `(token0, token1, fee)` hasn't been deployed. + return 1 + price = (quote_amount / (qty / (10 ** self.get_token(token0).decimals))) / 10 ** self.get_token(token1).decimals + + # calculate and subtract the realised fees from the price impact. See: + # https://github.com/uniswap-python/uniswap-python/issues/310 + price_impact_with_fees = float((spot_price - price) / spot_price) + fee_realised_percentage = realised_fee_percentage(fee, qty) + price_impact_real = price_impact_with_fees - fee_realised_percentage + + + def get_quote_exact_output_single(self, + token0: AddressLike, + token1: AddressLike, + qty: int, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK, + hook_data:bytes=bytes()) -> int: """Quote for token to token single hop trades with an exact output.""" if self.version == 4: if(token0 < token1): @@ -230,12 +361,21 @@ def get_quote_exact_output_single(self, token0: AddressLike, token1: AddressLike fee, tick_spacing, hooks,) - #[0] The input quote [1] estimated gas units used for the swap + #[0]=The input quote [1]=estimated gas units used for the swap price : int = self.quoter.functions.quoteExactOutputSingle((pool_key, zero_for_one,qty, hook_data)).call()[0] else: - raise ValueError("Function not supported for this version of Sunswap") + raise ValueError("Function is not supported for this version") return price + + def build_execute_params(self,): + """ + Generic parameters builder for universal router execute() call. + WIP + """ + pass + + #Swap functions def _token_to_token_swap_input(self, input_token: str, qty: int, @@ -285,7 +425,7 @@ def _token_to_token_swap_input(self, return self._build_and_send_tx(self.router.functions.execute(commands, inputs, self._deadline()), self._get_tx_params(value=ether_amount)) else: - raise ValueError + raise ValueError("Function is not supported for this version") def _token_to_token_swap_output(self, @@ -338,22 +478,24 @@ def _token_to_token_swap_output(self, return self._build_and_send_tx(self.router.functions.execute(commands, inputs, self._deadline()), self._get_tx_params(value=ether_amount)) else: - raise ValueError - + raise ValueError("Function is not supported for this version") + #Replaces pending transaction with zero-value ETH transfer def drop_txn(self, address_to: AddressLike, gwei: float, gasv: float, priorityfee: int=10) -> HexBytes: + #This one is for legacy transactions signed_txn = self.w3.eth.account.sign_transaction(dict(chainId=int(self.w3.net.version), nonce=self.last_nonce, gasPrice = Web3.to_wei(self.gas_price, 'gwei'), gas = int(self.gas_limit), to = Web3.to_checksum_address(address_to), value = Web3.to_wei(0,'wei')), self.private_key) + #This one is for post-Merge signed_txn_london = self.w3.eth.account.sign_transaction(dict(chainId=int(self.w3.net.version), type=2, nonce=self.last_nonce, @@ -389,7 +531,8 @@ def make_swap_output(self, return self._token_to_token_swap_output(swap_pool_key.currency0, qty, qtycap, swap_pool_key.currency1, recipient, swap_pool_key.fee, swap_pool_key.tick_spacing, swap_pool_key.hooks) - def get_token_balance(self, erc20: AddressLike) -> Decimal: + def get_token_balance(self, + erc20: AddressLike) -> Decimal: contract = _load_contract(self.w3, abi_name = "erc20", address = erc20) decimals = contract.functions.decimals().call() @@ -412,7 +555,9 @@ def _deadline(self) -> int: """Get a predefined deadline. 10min by default.""" return int(time.time()) + 10 * 60 - def _build_and_send_tx(self, function: ContractFunction, tx_params: Optional[dict]=None) -> HexBytes: + def _build_and_send_tx(self, + function: ContractFunction, + tx_params: Optional[dict]=None) -> HexBytes: """Build and send a transaction.""" if not tx_params: tx_params = self._get_tx_params() @@ -429,14 +574,3 @@ def _build_and_send_tx(self, function: ContractFunction, tx_params: Optional[dic self.last_nonce = Nonce(tx_params["nonce"] + 1) -def _load_contract(w3: Web3, abi_name: str, address: AddressLike) -> Contract: - address = Web3.to_checksum_address(address) - return w3.eth.contract(address=address, abi=_load_abi(abi_name)) - -def _load_abi(name: str) -> str: - path = f"{os.path.dirname(os.path.abspath(__file__))}/assets/" - with open(os.path.abspath(path + f"{name}.abi")) as f: - abi = json.load(f) - return abi - - diff --git a/uniswap/v4pools.py b/uniswap/v4pools.py new file mode 100644 index 00000000..ce29ea0e --- /dev/null +++ b/uniswap/v4pools.py @@ -0,0 +1,137 @@ +from typing import List, Any, Optional, Callable, Union, Tuple, Dict +from xml.etree import ElementTree +import configparser +from web3 import Web3 +import eth_abi.abi +from .uni4base import * +from .util import ( + _addr_to_str, + _load_contract, + _load_contract_erc20, + _str_to_addr, +) + + +class V4pools(): + """Uniswap V4 pools handler""" + def __init__(self, + web3: Web3,): + self.poolkeys_list = List() + self.web3 = web3 + self.last_block = 0 + + + def get_last_block(self,) -> int: + return self.last_block + + def set_last_block(self, value: int): + self.last_block = value + + def fetch_poolkey_data(self, first_block_number: int, chunk_size: int=500): + """ + :param pool_manager_contract_address PoolManager address on the network specified by w3 param + :param chunk_size defines amount of blocks per log request + """ + #Scans PoolManager contract initialize() logs in order to get list of + #all pools.See documentation for suggested starting blocks + # + #chunk_size default value should be suitable for public nodes; increase + #on private nodes for better performance + + chain_id = self.web3.net.version + config = configparser.ConfigParser() + config.read("configs\\poolmanager.ini") + pool_manager_contract_address = config.get("settings",chain_id) + pool_manager_contract = _load_contract(self.web3, "uniswap-v4/poolmanager", _str_to_addr(pool_manager_contract_address)) + last_block_number = this.web3.eth.get_block_number() + + chunks_amount = int((last_block_number - first_block_number) // chunk_size) + start_block = first_block_number + end_block = 0 + + print("Logs proccessing started.") + + for i in range(0, chunks_amount + 1): + #{ + print(f"Processing chunk {i}/{chunks_amount}",end='\r',flush=True) + if i < chunksAmount: + end_block = start_block + chunk_size + else: + end_block = this.web3.eth.get_block_number() + logs = pool_manager_contract.events.initialize().get_logs(fromBlock=start_block, toBlock=end_block,) + for log_item in logs: + txn = this.web3.eth.get_transaction_receipt(log_item["TransactionHash"]) + if txn["to"].lower() != pool_manager_contract_address.lower() or txn["status"] == 0: + continue + pool : pool_key = pool_key() + pool.currency0 = log_item["event"]["currency0"] + pool.currency1 = log_item["event"]["currency1"] + pool.fee = log_item["event"]["fee"] + pool.tick_spacing = log_item["event"]["tickSpacing"] + pool.hooks = log_item["event"]["hooks"] + self.poolkeys_list.append(pool) + start_block = start_block + chunk_size + + print(f"Logs proccessing completed. Last block processed {end_block}") + self.set_last_block(end_block) + return + + def save_poolkeys_list(self, poolkey_data_filename: str): + """Saves poolKey list to specified file (XML format supposed)""" + element_tree = ElementTree() + pool_data = element_tree.Element('PoolData') + + for item in self.poolkeys_list: + pool = element_tree.SubElement(pool_data, 'Pool') + + currency0 = element_tree.SubElement(pool, 'Currency0') + currency0.text = str(item[0]) + + currency1 = element_tree.SubElement(pool, 'Currency1') + currency1.text = str(item[1]) + + fee = element_tree.SubElement(pool, 'Fee') + fee.text = str(item[2]) + + tick_spacing = element_tree.SubElement(pool, 'TickSpacing') + tick_spacing.text = str(item[3]) + + hooks = element_tree.SubElement(pool, 'Hooks') + hooks.text = str(item[4]) + + final_data = element_tree.ElementTree(pool_data) + final_data.write(poolkey_data_filename) + return + + def load_poolkeys_list(self, poolkey_data_filename: str): + """Loads poolKey list from specified file (XML format supposed)""" + if(os.path.isfile(poolkey_data_filename)): + try: + element_tree = ElementTree() + tree = element_tree.parse(poolkey_data_filename) + except ParseError: + raise ValueError("Parse error, file seems to be corrupted (" + poolkey_data_filename + ")") + self.poolkeys_list.clear() + root = tree.getroot() + for item in root: + pool : pool_key = pool_key() + pool.currency0 = item[0].text + pool.currency1 = item[1].text + pool.fee = int(item[2].text) + pool.tick_spacing = int(item[3].text) + pool.hooks = item[4].text + self.poolkeys_list.append(pool) + else: + raise ValueError("Couldn't locate file " + poolkey_data_filename) + + + def get_poolkeys_sublist(self, currency0: str, currency1: str) -> List: + """Returns all pools for the (currency0, currency1) pair""" + if(currency0 < currency1): + (c0, c1) = (currency0, currency1) + else: + (c0, c1) = (currency1, currency0) + result_list = [x for x in self.poolkeys_list if c0 == x.currency0 and c1 == x.currency1] + return result_list + +