From 003e851a98d2ec3be363ee182c1fe302ef92571d Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:39:07 +0200 Subject: [PATCH 01/34] =?UTF-8?q?=F0=9F=92=84=20ContractUltil=20and=20Acco?= =?UTF-8?q?ntUtils=20refactoring=20to=20remove=20settings=20ref=20and=20us?= =?UTF-8?q?e=20it=20per=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/default_settings.toml | 80 ++++++++++++++++++++++++++++++++++++ dxsp/handler/client.py | 29 +++++++++---- dxsp/utils/account_utils.py | 37 +++++++---------- dxsp/utils/contract_utils.py | 24 ++++++----- 4 files changed, 129 insertions(+), 41 deletions(-) diff --git a/dxsp/default_settings.toml b/dxsp/default_settings.toml index ae9c6270..2679a0db 100644 --- a/dxsp/default_settings.toml +++ b/dxsp/default_settings.toml @@ -61,7 +61,22 @@ mapping = [ # mapping to use to change instrument per exchange { id = "BTC", alt = "WBTC" ,enable = true }, ] rotki_report_endpoint= "http://localhost:5042/api/v1/reports/1" +# Header to use for retrieving urls content below +headers = {User-Agent= 'Mozilla/5.0'} +# DEX ABI to use in case you have no explorer setup +dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" + +# token list using uniswap tokenlist format +# https://github.com/mraniki/tokenlist +# this can be used to avoid using coingecko +# or be used for niche token not listed anywhere +# or for testnet +# token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" +# token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" +token_mainnet_list = "" +token_testnet_list = "" +token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" # # uniswap v2 protocol type and router uniswap # [default.dex.eth] @@ -87,6 +102,23 @@ rotki_report_endpoint= "http://localhost:5042/api/v1/reports/1" # { id = "BTC", alt = "WBTC" }, # ] # rotki_report_endpoint= "http://localhost:5042/api/1/pnl" +# Header to use for retrieving urls content below +# headers = {User-Agent= 'Mozilla/5.0'} + +# # DEX ABI to use in case you have no explorer setup +# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" + +# # token list using uniswap tokenlist format +# # https://github.com/mraniki/tokenlist +# # this can be used to avoid using coingecko +# # or be used for niche token not listed anywhere +# # or for testnet +# # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" +# # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" +# token_mainnet_list = "" +# token_testnet_list = "" +# token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" + # # uniswap v2 protocol type and router pancakeswap on BSC chain 56 # [default.dex.bsc] @@ -112,6 +144,22 @@ rotki_report_endpoint= "http://localhost:5042/api/v1/reports/1" # { id = "BTC", alt = "BTCB" }, # ] # rotki_report_endpoint= "http://localhost:5042/api/1/pnl" +# headers = {User-Agent= 'Mozilla/5.0'} + +# # DEX ABI to use in case you have no explorer setup +# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" + +# # token list using uniswap tokenlist format +# # https://github.com/mraniki/tokenlist +# # this can be used to avoid using coingecko +# # or be used for niche token not listed anywhere +# # or for testnet +# # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" +# # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" +# token_mainnet_list = "" +# token_testnet_list = "" +# token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" + # # ZeroX 0x protocol type on polygon chain 137 # [default.dex.polygon] @@ -137,6 +185,22 @@ rotki_report_endpoint= "http://localhost:5042/api/v1/reports/1" # { id = "BTC", alt = "WBTC" }, # ] # rotki_report_endpoint= "http://localhost:5042/api/1/pnl" +# headers = {User-Agent= 'Mozilla/5.0'} + +# # DEX ABI to use in case you have no explorer setup +# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" + +# # token list using uniswap tokenlist format +# # https://github.com/mraniki/tokenlist +# # this can be used to avoid using coingecko +# # or be used for niche token not listed anywhere +# # or for testnet +# # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" +# # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" +# token_mainnet_list = "" +# token_testnet_list = "" +# token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" + # # kwenta protocol type on OPTIMISM chain 10 # [default.dex.opt] @@ -164,6 +228,22 @@ rotki_report_endpoint= "http://localhost:5042/api/v1/reports/1" # { id = "BTC", alt = "BTC" }, # ] # rotki_report_endpoint= "http://localhost:5042/api/1/pnl" +# headers = {User-Agent= 'Mozilla/5.0'} + +# # DEX ABI to use in case you have no explorer setup +# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" + +# # token list using uniswap tokenlist format +# # https://github.com/mraniki/tokenlist +# # this can be used to avoid using coingecko +# # or be used for niche token not listed anywhere +# # or for testnet +# # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" +# # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" +# token_mainnet_list = "" +# token_testnet_list = "" +# token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" + ######################################## ### END OF DEFAULT SETTINGS ### diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 7bf35a31..cbd8a963 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -84,6 +84,11 @@ def __init__(self, **kwargs): self.w3 = kwargs.get("w3", None) self.wallet_address = kwargs.get("wallet_address", None) self.private_key = kwargs.get("private_key", None) + self.headers = kwargs.get("headers", None) + self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.token_mainnet_list = kwargs.get("token_mainnet_list", None) + self.token_testnet_list = kwargs.get("token_testnet_list", None) + self.token_personal_list = kwargs.get("token_personal_list", None) self.router_contract_addr = kwargs.get("router_contract_addr", None) self.factory_contract_addr = kwargs.get("factory_contract_addr", None) self.trading_asset_address = kwargs.get("trading_asset_address", None) @@ -114,16 +119,24 @@ def __init__(self, **kwargs): ) logger.debug("Account {}", self.account_number) self.contract_utils = ContractUtils( - self.w3, self.block_explorer_url, self.block_explorer_api + w3=self.w3, + dex_erc20_abi_url=self.dex_erc20_abi_url, + token_mainnet_list=self.token_mainnet_list, + token_testnet_list=self.token_testnet_list, + token_personal_list=self.token_personal_list, + headers=self.headers, + block_explorer_url=self.block_explorer_url, + block_explorer_api=self.block_explorer_api, ) self.account = AccountUtils( - self.w3, - self.contract_utils, - self.wallet_address, - self.private_key, - self.trading_asset_address, - self.block_explorer_url, - self.block_explorer_api, + w3=self.w3, + contract_utils=self.contract_utils, + wallet_address=self.wallet_address, + private_key=self.private_key, + trading_asset_address=self.trading_asset_address, + router_contract_addr=self.router_contract_addr, + block_explorer_url=self.block_explorer_url, + block_explorer_api=self.block_explorer_api, ) else: self.account_number = None diff --git a/dxsp/utils/account_utils.py b/dxsp/utils/account_utils.py index 1595a0a1..1a4f9871 100644 --- a/dxsp/utils/account_utils.py +++ b/dxsp/utils/account_utils.py @@ -5,8 +5,6 @@ from loguru import logger -from dxsp.config import settings - class AccountUtils: """ @@ -31,27 +29,22 @@ class AccountUtils: """ - def __init__( - self, - w3, - contract_utils, - wallet_address, - private_key, - trading_asset_address, - block_explorer_url, - block_explorer_api, - safe=False, - ): - self.w3 = w3 - self.wallet_address = self.w3.to_checksum_address(wallet_address) + def __init__(self, **kwargs): + self.w3 = self.w3 = kwargs.get("w3", None) + self.wallet_address = self.w3.to_checksum_address( + kwargs.get("wallet_address", None) + ) self.account_number = ( f"{int(self.w3.net.version, 16)} - " f"{str(self.wallet_address)[-8:]}" ) - self.private_key = private_key - self.trading_asset_address = self.w3.to_checksum_address(trading_asset_address) - self.contract_utils = contract_utils - self.block_explorer_url = block_explorer_url - self.block_explorer_api = block_explorer_api + self.private_key = kwargs.get("private_key", None) + self.trading_asset_address = self.w3.to_checksum_address( + kwargs.get("trading_asset_address", None) + ) + self.router_contract_addr = kwargs.get("router_contract_addr", None) + self.contract_utils = kwargs.get("contract_utils", None) + self.block_explorer_url = kwargs.get("block_explorer_url", None) + self.block_explorer_api = kwargs.get("block_explorer_api", None) async def get_account_balance(self) -> str: """ @@ -147,9 +140,7 @@ async def get_approve(self, token_address): return approved_amount = self.w3.to_wei(2**64 - 1, "ether") owner_address = self.w3.to_checksum_address(self.wallet_address) - dex_router_address = self.w3.to_checksum_address( - settings.dex_router_contract_addr - ) + dex_router_address = self.w3.to_checksum_address(self.router_contract_addr) allowance = token.contract.functions.allowance( owner_address, dex_router_address ).call() diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 98640405..8ecbacb6 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -9,7 +9,6 @@ from loguru import logger from pycoingecko import CoinGeckoAPI -from dxsp.config import settings from dxsp.utils.utils import fetch_url @@ -35,7 +34,7 @@ class ContractUtils: """ - def __init__(self, w3=None, block_explorer_url=None, block_explorer_api=None): + def __init__(self, **kwargs): """ Initializes an instance of the class. @@ -48,10 +47,15 @@ def __init__(self, w3=None, block_explorer_url=None, block_explorer_api=None): :param block_explorer_api: The API endpoint of the block explorer. :type block_explorer_api: str """ - self.w3 = w3 + self.w3 = kwargs.get("w3", None) self.chain = int(self.w3.net.version, 16) - self.block_explorer_url = block_explorer_url - self.block_explorer_api = block_explorer_api + self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.token_mainnet_list = kwargs.get("token_mainnet_list", None) + self.token_testnet_list = kwargs.get("token_testnet_list", None) + self.token_personal_list = kwargs.get("token_personal_list", None) + self.headers = kwargs.get("headers", None) + self.block_explorer_url = kwargs.get("block_explorer_url", None) + self.block_explorer_api = kwargs.get("block_explorer_api", None) self.cg = CoinGeckoAPI() self.platform = self.get_cg_platform() logger.debug( @@ -184,9 +188,9 @@ async def get_tokenlist_data(self, symbol): """ try: token_list_urls = [ - settings.token_personal_list, - settings.token_testnet_list, - settings.token_mainnet_list, + self.token_personal_list, + self.token_testnet_list, + self.token_mainnet_list, ] for token_list_url in token_list_urls: @@ -385,7 +389,7 @@ async def get_token_abi(self, address=None): None if the request fails or the contract does not have an ABI. """ if not self.block_explorer_api: - return await fetch_url(settings.dex_erc20_abi_url) + return await fetch_url(self.dex_erc20_abi_url) if address is None: address = self.address params = { @@ -395,7 +399,7 @@ async def get_token_abi(self, address=None): "apikey": self.block_explorer_api, } resp = await fetch_url( - url=self.block_explorer_url, headers=settings.headers, params=params + url=self.block_explorer_url, headers=self.headers, params=params ) if resp: return resp["result"] if resp["status"] == "1" else None From 71f3dbbf4bf401958a1c2b9f60325a14c350979b Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:26:34 +0200 Subject: [PATCH 02/34] =?UTF-8?q?=F0=9F=90=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 2 +- dxsp/utils/contract_utils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index cbd8a963..5847e0a1 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -115,7 +115,7 @@ def __init__(self, **kwargs): if self.w3 and self.wallet_address: self.account_number = ( - f"{int(self.w3.net.version, 16)} - {str(self.wallet_address)[-8:]}" + f"{self.w3.net.version} - {str(self.wallet_address)[-8:]}" ) logger.debug("Account {}", self.account_number) self.contract_utils = ContractUtils( diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 8ecbacb6..fb229c1b 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -48,7 +48,8 @@ def __init__(self, **kwargs): :type block_explorer_api: str """ self.w3 = kwargs.get("w3", None) - self.chain = int(self.w3.net.version, 16) + # self.chain = int(self.w3.net.version, 16) + self.chain = self.w3.net.version self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) From 7398b1f9b5b9746ad37fdf8723f197f75ecda7a0 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:54:23 +0200 Subject: [PATCH 03/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Token=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 5 ++--- dxsp/utils/contract_utils.py | 29 ++++++++++++++--------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 5847e0a1..72b9dbf7 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -114,9 +114,8 @@ def __init__(self, **kwargs): logger.error(f"Failed to connect to RPC: {e}") if self.w3 and self.wallet_address: - self.account_number = ( - f"{self.w3.net.version} - {str(self.wallet_address)[-8:]}" - ) + self.chain = self.w3.net.version + self.account_number = f"{self.chain} - {str(self.wallet_address)[-8:]}" logger.debug("Account {}", self.account_number) self.contract_utils = ContractUtils( w3=self.w3, diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index fb229c1b..6d503302 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -48,8 +48,8 @@ def __init__(self, **kwargs): :type block_explorer_api: str """ self.w3 = kwargs.get("w3", None) - # self.chain = int(self.w3.net.version, 16) - self.chain = self.w3.net.version + self.chain = int(self.w3.net.version, 16) + # self.chain = self.w3.net.version self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) @@ -81,6 +81,10 @@ async def get_data(self, symbol=None, contract_address=None): token = Token( w3=self.w3, address=contract_address, + abi_url=self.dex_erc20_abi_url, + block_explorer_url=self.block_explorer_url, + block_explorer_api=self.block_explorer_api, + headers=self.headers, ) await token.fetch_data() return token @@ -324,14 +328,7 @@ class Token: """ - def __init__( - self, - w3=None, - address=None, - block_explorer_url=None, - block_explorer_api=None, - symbol=None, - ): + def __init__(self, **kwargs): """ Initializes an instance of the class. @@ -351,11 +348,13 @@ def __init__( :type symbol: str """ try: - self.w3 = w3 - self.address = self.w3.to_checksum_address(address) - self.symbol = symbol - self.block_explorer_url = block_explorer_url - self.block_explorer_api = block_explorer_api + self.w3 = kwargs.get("w3", None) + self.address = self.w3.to_checksum_address(kwargs.get("address", None)) + self.symbol = kwargs.get("symbol", None) + self.headers = kwargs.get("headers", None) + self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.block_explorer_url = kwargs.get("block_explorer_url", None) + self.block_explorer_api = kwargs.get("block_explorer_api", None) self.decimals = None self.name = None logger.debug( From 82f85ce76344708fc20b05817e96296aa153aa9a Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:08:13 +0200 Subject: [PATCH 04/34] =?UTF-8?q?=F0=9F=90=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/contract_utils.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 6d503302..ece091e8 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -81,10 +81,10 @@ async def get_data(self, symbol=None, contract_address=None): token = Token( w3=self.w3, address=contract_address, - abi_url=self.dex_erc20_abi_url, + headers=self.headers, + dex_erc20_abi_url=self.dex_erc20_abi_url, block_explorer_url=self.block_explorer_url, block_explorer_api=self.block_explorer_api, - headers=self.headers, ) await token.fetch_data() return token @@ -175,7 +175,14 @@ async def search_tokenlist_data(self, token): """ result = await self.get_tokenlist_data(token) if result is not None: - token_instance = Token(w3=self.w3, address=result["address"]) + token_instance = Token( + w3=self.w3, + address=result["address"], + headers=self.headers, + dex_erc20_abi_url=self.dex_erc20_abi_url, + block_explorer_api=self.block_explorer_api, + block_explorer_url=self.block_explorer_url, + ) token_instance.decimals = result["decimals"] token_instance.symbol = result["symbol"] return token_instance @@ -229,7 +236,14 @@ async def search_cg_data(self, token): result = await self.get_cg_data(token) if result is not None: logger.info("Found on Coingecko") - token_instance = Token(w3=self.w3, address=result["contract_address"]) + token_instance = Token( + w3=self.w3, + address=result["contract_address"], + headers=self.headers, + dex_erc20_abi_url=self.dex_erc20_abi_url, + block_explorer_api=self.block_explorer_api, + block_explorer_url=self.block_explorer_url, + ) token_instance.decimals = result["decimal_place"] return token_instance return None From 2ca28dcc73c4d64dcbeed51da3d6c6a7637fbaa3 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:44:31 +0200 Subject: [PATCH 05/34] =?UTF-8?q?=F0=9F=92=84=20TokenUtils=20created?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/__init__.py | 2 + dxsp/utils/contract_utils.py | 197 +---------------------------------- dxsp/utils/token_utils.py | 195 ++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 192 deletions(-) create mode 100644 dxsp/utils/token_utils.py diff --git a/dxsp/utils/__init__.py b/dxsp/utils/__init__.py index 8e257387..e1ce69eb 100644 --- a/dxsp/utils/__init__.py +++ b/dxsp/utils/__init__.py @@ -1,7 +1,9 @@ from dxsp.utils.account_utils import AccountUtils from dxsp.utils.contract_utils import ContractUtils +from dxsp.utils.token_utils import Token __all__ = [ "AccountUtils", "ContractUtils", + "Token", ] diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index ece091e8..9a3a9170 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -9,6 +9,7 @@ from loguru import logger from pycoingecko import CoinGeckoAPI +from dxsp.utils.token_utils import Token from dxsp.utils.utils import fetch_url @@ -47,9 +48,11 @@ def __init__(self, **kwargs): :param block_explorer_api: The API endpoint of the block explorer. :type block_explorer_api: str """ + logger.debug("Initializing ContractUtils") + logger.debug("kwargs: {}", kwargs) + self.w3 = kwargs.get("w3", None) self.chain = int(self.w3.net.version, 16) - # self.chain = self.w3.net.version self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) @@ -209,7 +212,7 @@ async def get_tokenlist_data(self, symbol): if not token_list_url: continue logger.debug("Token search in {}", token_list_url) - token_list = await fetch_url(token_list_url) + token_list = await fetch_url(url=token_list_url) token_search = token_list["tokens"] for keyval in token_search: if keyval["symbol"] == symbol and keyval["chainId"] == self.chain: @@ -312,193 +315,3 @@ async def get_confirmation(self, transaction_hash): } except Exception as error: logger.error("get_confirmation {}", error) - - -class Token: - """ - - Class Token to interact with web3 token contract - - Args: - w3: An instance of the web3 library. - address: The address of the token contract. - block_explorer_url: The URL of the block explorer for the token. - block_explorer_api: The API endpoint of the block explorer for the token. - symbol: The symbol of the token. - - Returns: - Token - - Methods: - __init__: Initializes an instance of the class. - fetch_data: Retrieves data for the token. - get_token_abi: Retrieves the token abi. - get_token_contract: Retrieves the token contract. - get_contract_function: Retrieves the contract functions by name. - get_token_balance: Retrieves the token balance. - get_token_symbol: Retrieves the token symbol. - get_token_name: Retrieves the token name. - get_token_decimals: Retrieves the token decimals. - - """ - - def __init__(self, **kwargs): - """ - Initializes an instance of the class. - - :param w3: An instance of the web3 library. - :type w3: object - - :param address: The address of the token contract. - :type address: str - - :param block_explorer_url: The URL of the block explorer for the token. - :type block_explorer_url: str - - :param block_explorer_api: The API endpoint of the block explorer for the token. - :type block_explorer_api: str - - :param symbol: The symbol of the token. - :type symbol: str - """ - try: - self.w3 = kwargs.get("w3", None) - self.address = self.w3.to_checksum_address(kwargs.get("address", None)) - self.symbol = kwargs.get("symbol", None) - self.headers = kwargs.get("headers", None) - self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) - self.block_explorer_url = kwargs.get("block_explorer_url", None) - self.block_explorer_api = kwargs.get("block_explorer_api", None) - self.decimals = None - self.name = None - logger.debug( - "token initialized symbol {} address {}", self.symbol, self.address - ) - except Exception as error: - logger.error("token error {}", error) - - async def fetch_data(self) -> None: - """ - Retrieves data for the token. - """ - logger.debug("fetch token data") - self.contract = await self.get_token_contract() - self.decimals = await self.get_token_decimals() - self.symbol = await self.get_token_symbol() - self.name = await self.get_token_name() - logger.debug("{} - token data {}", self.symbol, self.address) - - async def get_token_abi(self, address=None): - """ - Retrieves the ABI (Application Binary Interface) - of a token contract at the given address. - - Args: - address (str, optional): The address of the token contract. - If not provided, the address associated with the - contract instance is used. Defaults to None. - - Returns: - str: The ABI of the token contract, if successful. - None if the request fails or the contract does not have an ABI. - """ - if not self.block_explorer_api: - return await fetch_url(self.dex_erc20_abi_url) - if address is None: - address = self.address - params = { - "module": "contract", - "action": "getabi", - "address": address, - "apikey": self.block_explorer_api, - } - resp = await fetch_url( - url=self.block_explorer_url, headers=self.headers, params=params - ) - if resp: - return resp["result"] if resp["status"] == "1" else None - - async def get_token_contract(self): - """ - Retrieves the token contract. - - :return: The token contract. - """ - self.abi = await self.get_token_abi() - if self.abi is None: - return None - contract = self.w3.eth.contract(address=self.address, abi=self.abi) - return contract - - def get_contract_function(self, contract, func_name: str): - """ - Get the contract function by name. - - Args: - contract: The contract object. - func_name (str): The name of the function. - - Returns: - bool: True if the function exists - in the contract, False otherwise. - """ - return func_name in dir(contract.functions) - - async def get_token_balance(self, wallet_address): - """ - Get the balance of a token for a given wallet address. - - Args: - wallet_address (str): The wallet - address to check the balance for. - - Returns: - float: The balance of the token in ether. - - Raises: - None - """ - contract = await self.get_token_contract() - if contract is None or contract.functions is None: - logger.warning("No Balance") - return 0 - balance = contract.functions.balanceOf(wallet_address).call() - if balance is None: - logger.warning("No Balance") - return 0 - return round(self.w3.from_wei(balance, "ether"), 5) or 0 - - async def get_token_symbol(self): - """ - Retrieves the symbol of the token. - - Returns: - str: The symbol of the token. - """ - contract = await self.get_token_contract() - return contract.functions.symbol().call() - - async def get_token_name(self): - """ - Get the name of the token. - - Args: - self: The object itself. - - Returns: - The name of the token as a string. - """ - contract = await self.get_token_contract() - return contract.functions.name().call() - - async def get_token_decimals(self): - """ - Get the number of decimal places for the token. - - Returns: - int: The number of decimal places for the token. - """ - if self.decimals is not None: - return self.decimals - contract = await self.get_token_contract() - return contract.functions.decimals().call() if contract else 18 diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py new file mode 100644 index 00000000..0c6e538e --- /dev/null +++ b/dxsp/utils/token_utils.py @@ -0,0 +1,195 @@ +from loguru import logger + +from dxsp.utils.utils import fetch_url + + +class Token: + """ + + Class Token to interact with web3 token contract + + Args: + w3: An instance of the web3 library. + address: The address of the token contract. + block_explorer_url: The URL of the block explorer for the token. + block_explorer_api: The API endpoint of the block explorer for the token. + symbol: The symbol of the token. + + Returns: + Token + + Methods: + __init__: Initializes an instance of the class. + fetch_data: Retrieves data for the token. + get_token_abi: Retrieves the token abi. + get_token_contract: Retrieves the token contract. + get_contract_function: Retrieves the contract functions by name. + get_token_balance: Retrieves the token balance. + get_token_symbol: Retrieves the token symbol. + get_token_name: Retrieves the token name. + get_token_decimals: Retrieves the token decimals. + + """ + + def __init__(self, **kwargs): + """ + Initializes an instance of the class. + + :param w3: An instance of the web3 library. + :type w3: object + + :param address: The address of the token contract. + :type address: str + + :param block_explorer_url: The URL of the block explorer for the token. + :type block_explorer_url: str + + :param block_explorer_api: The API endpoint of the block explorer for the token. + :type block_explorer_api: str + + :param symbol: The symbol of the token. + :type symbol: str + """ + try: + + logger.debug("token init {} {}", kwargs, type(kwargs)) + self.w3 = kwargs.get("w3", None) + self.address = self.w3.to_checksum_address(kwargs.get("address", None)) + self.symbol = kwargs.get("symbol", None) + self.headers = kwargs.get("headers", None) + self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.block_explorer_url = kwargs.get("block_explorer_url", None) + self.block_explorer_api = kwargs.get("block_explorer_api", None) + self.decimals = None + self.name = None + logger.debug( + "token initialized symbol {} address {}", self.symbol, self.address + ) + except Exception as error: + logger.error("token error {}", error) + + async def fetch_data(self) -> None: + """ + Retrieves data for the token. + """ + logger.debug("fetch token data") + self.contract = await self.get_token_contract() + self.decimals = await self.get_token_decimals() + self.symbol = await self.get_token_symbol() + self.name = await self.get_token_name() + logger.debug("{} - token data {}", self.symbol, self.address) + + async def get_token_abi(self, address=None): + """ + Retrieves the ABI (Application Binary Interface) + of a token contract at the given address. + + Args: + address (str, optional): The address of the token contract. + If not provided, the address associated with the + contract instance is used. Defaults to None. + + Returns: + str: The ABI of the token contract, if successful. + None if the request fails or the contract does not have an ABI. + """ + if not self.block_explorer_api: + return await fetch_url(url=self.dex_erc20_abi_url) + if address is None: + address = self.address + params = { + "module": "contract", + "action": "getabi", + "address": address, + "apikey": self.block_explorer_api, + } + resp = await fetch_url( + url=self.block_explorer_url, headers=self.headers, params=params + ) + if resp: + return resp["result"] if resp["status"] == "1" else None + + async def get_token_contract(self): + """ + Retrieves the token contract. + + :return: The token contract. + """ + self.abi = await self.get_token_abi() + if self.abi is None: + return None + contract = self.w3.eth.contract(address=self.address, abi=self.abi) + return contract + + def get_contract_function(self, contract, func_name: str): + """ + Get the contract function by name. + + Args: + contract: The contract object. + func_name (str): The name of the function. + + Returns: + bool: True if the function exists + in the contract, False otherwise. + """ + return func_name in dir(contract.functions) + + async def get_token_balance(self, wallet_address): + """ + Get the balance of a token for a given wallet address. + + Args: + wallet_address (str): The wallet + address to check the balance for. + + Returns: + float: The balance of the token in ether. + + Raises: + None + """ + contract = await self.get_token_contract() + if contract is None or contract.functions is None: + logger.warning("No Balance") + return 0 + balance = contract.functions.balanceOf(wallet_address).call() + if balance is None: + logger.warning("No Balance") + return 0 + return round(self.w3.from_wei(balance, "ether"), 5) or 0 + + async def get_token_symbol(self): + """ + Retrieves the symbol of the token. + + Returns: + str: The symbol of the token. + """ + contract = await self.get_token_contract() + return contract.functions.symbol().call() + + async def get_token_name(self): + """ + Get the name of the token. + + Args: + self: The object itself. + + Returns: + The name of the token as a string. + """ + contract = await self.get_token_contract() + return contract.functions.name().call() + + async def get_token_decimals(self): + """ + Get the number of decimal places for the token. + + Returns: + int: The number of decimal places for the token. + """ + if self.decimals is not None: + return self.decimals + contract = await self.get_token_contract() + return contract.functions.decimals().call() if contract else 18 From c8a738972d67a2587693f20d368f7dc3ab4d03d3 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:10:59 +0200 Subject: [PATCH 06/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/token_utils.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py index 0c6e538e..f99f75a1 100644 --- a/dxsp/utils/token_utils.py +++ b/dxsp/utils/token_utils.py @@ -83,6 +83,9 @@ async def get_token_abi(self, address=None): """ Retrieves the ABI (Application Binary Interface) of a token contract at the given address. + First, it tries to fetch the ABI from the block explorer + if the block explorer url and api key are provided. + If not, it tries to fetch the ABI from the dex ERC20 ABI URL Args: address (str, optional): The address of the token contract. @@ -93,21 +96,23 @@ async def get_token_abi(self, address=None): str: The ABI of the token contract, if successful. None if the request fails or the contract does not have an ABI. """ - if not self.block_explorer_api: - return await fetch_url(url=self.dex_erc20_abi_url) - if address is None: - address = self.address - params = { - "module": "contract", - "action": "getabi", - "address": address, - "apikey": self.block_explorer_api, - } - resp = await fetch_url( - url=self.block_explorer_url, headers=self.headers, params=params - ) - if resp: - return resp["result"] if resp["status"] == "1" else None + if self.block_explorer_url and self.block_explorer_api: + if address is None: + address = self.address + params = { + "module": "contract", + "action": "getabi", + "address": address, + "apikey": self.block_explorer_api, + } + resp = await fetch_url( + url=self.block_explorer_url, headers=self.headers, params=params + ) + if resp: + return resp["result"] if resp["status"] == "1" else None + + logger.warning("No block explorer url or api key provided") + return await fetch_url(url=self.dex_erc20_abi_url) async def get_token_contract(self): """ @@ -116,6 +121,7 @@ async def get_token_contract(self): :return: The token contract. """ self.abi = await self.get_token_abi() + logger.debug("token abi {}", self.abi) if self.abi is None: return None contract = self.w3.eth.contract(address=self.address, abi=self.abi) From 31a50f8a22f6d79ac472c7aac0923f630c4c6362 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:33:41 +0200 Subject: [PATCH 07/34] =?UTF-8?q?=F0=9F=92=A5=20breaking=20due=20to=20abi?= =?UTF-8?q?=5Furl=20and=20block=20explorer=20new=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/default_settings.toml | 10 +++++----- dxsp/handler/client.py | 4 ++-- dxsp/utils/contract_utils.py | 10 +++++----- dxsp/utils/token_utils.py | 20 +++++++++++--------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/dxsp/default_settings.toml b/dxsp/default_settings.toml index 2679a0db..eab8d691 100644 --- a/dxsp/default_settings.toml +++ b/dxsp/default_settings.toml @@ -65,7 +65,7 @@ rotki_report_endpoint= "http://localhost:5042/api/v1/reports/1" headers = {User-Agent= 'Mozilla/5.0'} # DEX ABI to use in case you have no explorer setup -dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" +abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" # token list using uniswap tokenlist format # https://github.com/mraniki/tokenlist @@ -106,7 +106,7 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # headers = {User-Agent= 'Mozilla/5.0'} # # DEX ABI to use in case you have no explorer setup -# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" +# abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" # # token list using uniswap tokenlist format # # https://github.com/mraniki/tokenlist @@ -147,7 +147,7 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # headers = {User-Agent= 'Mozilla/5.0'} # # DEX ABI to use in case you have no explorer setup -# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" +# abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" # # token list using uniswap tokenlist format # # https://github.com/mraniki/tokenlist @@ -188,7 +188,7 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # headers = {User-Agent= 'Mozilla/5.0'} # # DEX ABI to use in case you have no explorer setup -# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" +# abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" # # token list using uniswap tokenlist format # # https://github.com/mraniki/tokenlist @@ -231,7 +231,7 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # headers = {User-Agent= 'Mozilla/5.0'} # # DEX ABI to use in case you have no explorer setup -# dex_erc20_abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" +# abi_url = "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json" # # token list using uniswap tokenlist format # # https://github.com/mraniki/tokenlist diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 72b9dbf7..8b23df85 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -85,7 +85,7 @@ def __init__(self, **kwargs): self.wallet_address = kwargs.get("wallet_address", None) self.private_key = kwargs.get("private_key", None) self.headers = kwargs.get("headers", None) - self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.abi_url = kwargs.get("abi_url", None) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) self.token_personal_list = kwargs.get("token_personal_list", None) @@ -119,7 +119,7 @@ def __init__(self, **kwargs): logger.debug("Account {}", self.account_number) self.contract_utils = ContractUtils( w3=self.w3, - dex_erc20_abi_url=self.dex_erc20_abi_url, + abi_url=self.abi_url, token_mainnet_list=self.token_mainnet_list, token_testnet_list=self.token_testnet_list, token_personal_list=self.token_personal_list, diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 9a3a9170..23163a4e 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -49,11 +49,11 @@ def __init__(self, **kwargs): :type block_explorer_api: str """ logger.debug("Initializing ContractUtils") - logger.debug("kwargs: {}", kwargs) + # logger.debug("kwargs: {}", kwargs) self.w3 = kwargs.get("w3", None) self.chain = int(self.w3.net.version, 16) - self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.abi_url = kwargs.get("abi_url", None) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) self.token_personal_list = kwargs.get("token_personal_list", None) @@ -85,7 +85,7 @@ async def get_data(self, symbol=None, contract_address=None): w3=self.w3, address=contract_address, headers=self.headers, - dex_erc20_abi_url=self.dex_erc20_abi_url, + abi_url=self.abi_url, block_explorer_url=self.block_explorer_url, block_explorer_api=self.block_explorer_api, ) @@ -182,7 +182,7 @@ async def search_tokenlist_data(self, token): w3=self.w3, address=result["address"], headers=self.headers, - dex_erc20_abi_url=self.dex_erc20_abi_url, + abi_url=self.abi_url, block_explorer_api=self.block_explorer_api, block_explorer_url=self.block_explorer_url, ) @@ -243,7 +243,7 @@ async def search_cg_data(self, token): w3=self.w3, address=result["contract_address"], headers=self.headers, - dex_erc20_abi_url=self.dex_erc20_abi_url, + abi_url=self.abi_url, block_explorer_api=self.block_explorer_api, block_explorer_url=self.block_explorer_url, ) diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py index f99f75a1..2a4ef854 100644 --- a/dxsp/utils/token_utils.py +++ b/dxsp/utils/token_utils.py @@ -52,12 +52,12 @@ def __init__(self, **kwargs): """ try: - logger.debug("token init {} {}", kwargs, type(kwargs)) + # logger.debug("token init {} {}", kwargs, type(kwargs)) self.w3 = kwargs.get("w3", None) self.address = self.w3.to_checksum_address(kwargs.get("address", None)) self.symbol = kwargs.get("symbol", None) self.headers = kwargs.get("headers", None) - self.dex_erc20_abi_url = kwargs.get("dex_erc20_abi_url", None) + self.abi_url = kwargs.get("abi_url", None) self.block_explorer_url = kwargs.get("block_explorer_url", None) self.block_explorer_api = kwargs.get("block_explorer_api", None) self.decimals = None @@ -73,10 +73,10 @@ async def fetch_data(self) -> None: Retrieves data for the token. """ logger.debug("fetch token data") - self.contract = await self.get_token_contract() - self.decimals = await self.get_token_decimals() - self.symbol = await self.get_token_symbol() - self.name = await self.get_token_name() + self.contract = await self.get_token_contract() or None + self.decimals = await self.get_token_decimals() or 18 + self.symbol = await self.get_token_symbol() or None + self.name = await self.get_token_name() or None logger.debug("{} - token data {}", self.symbol, self.address) async def get_token_abi(self, address=None): @@ -112,7 +112,7 @@ async def get_token_abi(self, address=None): return resp["result"] if resp["status"] == "1" else None logger.warning("No block explorer url or api key provided") - return await fetch_url(url=self.dex_erc20_abi_url) + return await fetch_url(url=self.abi_url) async def get_token_contract(self): """ @@ -173,7 +173,8 @@ async def get_token_symbol(self): str: The symbol of the token. """ contract = await self.get_token_contract() - return contract.functions.symbol().call() + if contract: + return contract.functions.symbol().call() async def get_token_name(self): """ @@ -186,7 +187,8 @@ async def get_token_name(self): The name of the token as a string. """ contract = await self.get_token_contract() - return contract.functions.name().call() + if contract: + return contract.functions.name().call() async def get_token_decimals(self): """ From 7204e30ae4b2a704fe71f793f196e9f04910c2fa Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 19:17:54 +0200 Subject: [PATCH 08/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/default_settings.toml | 10 +--------- dxsp/handler/client.py | 16 +++++++++++----- dxsp/handler/uniswap.py | 1 - dxsp/handler/zerox.py | 15 ++++++++++----- dxsp/main.py | 13 ++++--------- dxsp/utils/token_utils.py | 6 ++++++ 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/dxsp/default_settings.toml b/dxsp/default_settings.toml index eab8d691..e722e728 100644 --- a/dxsp/default_settings.toml +++ b/dxsp/default_settings.toml @@ -115,8 +115,6 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # # or for testnet # # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" # # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" -# token_mainnet_list = "" -# token_testnet_list = "" # token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" @@ -156,8 +154,6 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # # or for testnet # # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" # # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" -# token_mainnet_list = "" -# token_testnet_list = "" # token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" @@ -197,8 +193,6 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # # or for testnet # # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" # # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" -# token_mainnet_list = "" -# token_testnet_list = "" # token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" @@ -240,9 +234,7 @@ token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/ # # or for testnet # # token_mainnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/all.json" # # token_testnet_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/testnet.json" -# token_mainnet_list = "" -# token_testnet_list = "" -# token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" +# # token_personal_list = "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json" ######################################## diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 8b23df85..59563baa 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -76,19 +76,25 @@ def __init__(self, **kwargs): self.name = kwargs.get("name", None) logger.debug(f"Setting up: {self.name}") - self.protocol = kwargs.get("protocol") or "uniswap" - self.protocol_version = kwargs.get("protocol_version") or 2 + self.protocol = kwargs.get("library") or kwargs.get("protocol") or "uniswap" + self.protocol_version = kwargs.get("protocol_version", 2) self.api_endpoint = kwargs.get("api_endpoint", None) self.api_key = kwargs.get("api_key", None) self.rpc = kwargs.get("rpc", None) self.w3 = kwargs.get("w3", None) self.wallet_address = kwargs.get("wallet_address", None) self.private_key = kwargs.get("private_key", None) - self.headers = kwargs.get("headers", None) - self.abi_url = kwargs.get("abi_url", None) + self.headers = kwargs.get("headers", "{User-Agent= 'Mozilla/5.0'}") + self.abi_url = kwargs.get( + "abi_url", + "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json", + ) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) - self.token_personal_list = kwargs.get("token_personal_list", None) + self.token_personal_list = kwargs.get( + "token_personal_list", + "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json", + ) self.router_contract_addr = kwargs.get("router_contract_addr", None) self.factory_contract_addr = kwargs.get("factory_contract_addr", None) self.trading_asset_address = kwargs.get("trading_asset_address", None) diff --git a/dxsp/handler/uniswap.py b/dxsp/handler/uniswap.py index 46bd781c..dacabc38 100644 --- a/dxsp/handler/uniswap.py +++ b/dxsp/handler/uniswap.py @@ -54,7 +54,6 @@ def build_client(self): web3=self.w3, factory_contract_addr=self.factory_contract_addr, router_contract_addr=self.router_contract_addr, - # enable_caching=True, ) logger.debug("Uniswap client {}", self.client) except Exception as error: diff --git a/dxsp/handler/zerox.py b/dxsp/handler/zerox.py index 2c1587dc..5c9e3ca7 100644 --- a/dxsp/handler/zerox.py +++ b/dxsp/handler/zerox.py @@ -27,7 +27,6 @@ def __init__( """ super().__init__(**kwargs) - # self.build_client() self.client = "0x" async def get_quote( @@ -90,6 +89,7 @@ async def get_quote( return response["code"], response["reason"] except Exception as error: logger.error("Quote failed {}", error) + return f"⚠️ {error}" async def make_swap(self, buy_address, sell_address, amount): """ @@ -108,7 +108,12 @@ async def make_swap(self, buy_address, sell_address, amount): of the `account` object with the `swap_order` as an argument. """ - logger.debug(f"0x make_swap {buy_address} {sell_address} {amount}") - swap_order = await self.get_quote(buy_address, sell_address, amount) - if swap_order: - return await self.account.get_sign(swap_order) + try: + logger.debug(f"0x make_swap {buy_address} {sell_address} {amount}") + swap_order = await self.get_quote(buy_address, sell_address, amount) + if swap_order: + return await self.account.get_sign(swap_order) + + except Exception as error: + logger.error("Swap failed {}", error) + return f"⚠️ {error}" diff --git a/dxsp/main.py b/dxsp/main.py index b5ef05a7..bf5ec34a 100644 --- a/dxsp/main.py +++ b/dxsp/main.py @@ -216,8 +216,6 @@ async def get_pnls(self, **kwargs): _info.append(client_info) return "\n".join(_info) - get_pnl = get_pnls - async def get_quotes(self, symbol=None, address=None): """ gets a quote for a token @@ -231,13 +229,10 @@ async def get_quotes(self, symbol=None, address=None): """ _info = [] for client in self.clients: - try: - quote = await client.get_quote(sell_symbol=symbol, sell_address=address) - client_info = f"{client.name}: {quote}" - _info.append(client_info) - logger.debug("Retrieved quote for {}: {}", client.name, quote) - except Exception as error: - logger.error("Error retrieving quote for {}: {}", client.name, error) + quote = await client.get_quote(sell_symbol=symbol, sell_address=address) + client_info = f"{client.name}: {quote}" + _info.append(client_info) + logger.debug("Retrieved quote for {}: {}", client.name, quote) logger.debug("All quotes: {}", " | ".join(_info)) return "\n".join(_info) diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py index 2a4ef854..1943290d 100644 --- a/dxsp/utils/token_utils.py +++ b/dxsp/utils/token_utils.py @@ -1,3 +1,9 @@ +""" + DEX SWAP +🪙 TOKEN +""" + + from loguru import logger from dxsp.utils.utils import fetch_url From b7b1e73a8650cce6159baeca5c2f01b9fa3e62ed Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 19:29:17 +0200 Subject: [PATCH 09/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/contract_utils.py | 60 +++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 23163a4e..d9182a87 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -5,7 +5,6 @@ from datetime import datetime -import requests from loguru import logger from pycoingecko import CoinGeckoAPI @@ -49,23 +48,38 @@ def __init__(self, **kwargs): :type block_explorer_api: str """ logger.debug("Initializing ContractUtils") - # logger.debug("kwargs: {}", kwargs) - - self.w3 = kwargs.get("w3", None) - self.chain = int(self.w3.net.version, 16) - self.abi_url = kwargs.get("abi_url", None) - self.token_mainnet_list = kwargs.get("token_mainnet_list", None) - self.token_testnet_list = kwargs.get("token_testnet_list", None) - self.token_personal_list = kwargs.get("token_personal_list", None) - self.headers = kwargs.get("headers", None) - self.block_explorer_url = kwargs.get("block_explorer_url", None) - self.block_explorer_api = kwargs.get("block_explorer_api", None) - self.cg = CoinGeckoAPI() - self.platform = self.get_cg_platform() + + # Use local variable to reduce repeated dictionary lookups + get = kwargs.get + + self.w3 = get("w3", None) + # Directly convert to int, assuming w3 is always provided + self.chain = int(self.w3.net.version) if self.w3 else None + self.abi_url = get("abi_url", None) + self.token_mainnet_list = get("token_mainnet_list", None) + self.token_testnet_list = get("token_testnet_list", None) + self.token_personal_list = get("token_personal_list", None) + self.headers = get("headers", None) + self.block_explorer_url = get("block_explorer_url", None) + self.block_explorer_api = get("block_explorer_api", None) + self.cg = None + self.platform = None + + # Logging for debugging purpose logger.debug( "w3: {}. chain: {}. platform: {}", self.w3, self.chain, self.platform ) + def initialize_platform(self): + """ + Initialize the platform by making an API call to CoinGecko. + Call this method when the platform information is actually needed. + """ + if self.platform is None: + self.cg = CoinGeckoAPI() + self.platform = self.get_cg_platform() + logger.debug("Platform initialized: {}", self.platform) + async def get_data(self, symbol=None, contract_address=None): """ Get data based on the provided symbol or contract address. @@ -262,22 +276,10 @@ async def get_cg_data(self, token): str or None: The data for the token on the specified platform, or None if the token is not found or an error occurs. """ - # todo: add support for address search try: - if self.platform is None: - return None - search_results = self.cg.search(query=token) - search_dict = search_results["coins"] - # logger.debug("Coingecko search results: {}", search_dict) - filtered_dict = [x for x in search_dict if x["symbol"] == token.upper()] - api_dict = [sub["api_symbol"] for sub in filtered_dict] - for i in api_dict: - coin_dict = self.cg.get_coin_by_id(i) - try: - if coin_dict["detail_platforms"][f"{self.platform}"]: - return coin_dict["detail_platforms"][f"{self.platform}"] - except (KeyError, requests.exceptions.HTTPError): - pass + self.initialize_platform() + coin_dict = self.cg.get_coin_by_id(token.upper()) + return coin_dict.get("detail_platforms", {}).get(self.platform) except Exception as e: logger.error("get_cg_data {}", e) From 9e61478fcd1dab4bdc4bfb89b31c2d3f7c7805af Mon Sep 17 00:00:00 2001 From: mraniki Date: Sat, 13 Jul 2024 20:11:33 +0200 Subject: [PATCH 10/34] Update function call in test_unit_dexswap.py, remove unused code in test_unit_client.py --- tests/test_unit_client.py | 5 ----- tests/test_unit_dexswap.py | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 735dba02..4367f8d7 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -20,11 +20,6 @@ def DexSwap_fixture(): return DexSwap() -# @pytest.fixture(name="dex_client") -# def client_fixture(dex): -# for dx in dex.clients: -# if dx.protocol == "uniswap": -# return dx @pytest.fixture(name="dex_client") diff --git a/tests/test_unit_dexswap.py b/tests/test_unit_dexswap.py index 1c5e307d..84349ece 100755 --- a/tests/test_unit_dexswap.py +++ b/tests/test_unit_dexswap.py @@ -128,7 +128,7 @@ async def test_get_positions(dex): @pytest.mark.asyncio async def test_get_pnls(dex): get_account_pnl = AsyncMock() - result = await dex.get_pnl() + result = await dex.get_pnls() assert result is not None assert get_account_pnl.awaited assert ("eth" in result) or ("pol" in result) From 0807e3a274a9c0e2c56b0d7a0a4aaacd98854150 Mon Sep 17 00:00:00 2001 From: mraniki Date: Sat, 13 Jul 2024 20:32:25 +0200 Subject: [PATCH 11/34] Refactor token_personal_list initialization in DexClient --- dxsp/handler/client.py | 5 +---- dxsp/utils/contract_utils.py | 2 +- tests/test_unit_dexswap.py | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 59563baa..450d6abe 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -91,10 +91,7 @@ def __init__(self, **kwargs): ) self.token_mainnet_list = kwargs.get("token_mainnet_list", None) self.token_testnet_list = kwargs.get("token_testnet_list", None) - self.token_personal_list = kwargs.get( - "token_personal_list", - "https://raw.githubusercontent.com/mraniki/tokenlist/main/TT.json", - ) + self.token_personal_list = kwargs.get("token_personal_list", None) self.router_contract_addr = kwargs.get("router_contract_addr", None) self.factory_contract_addr = kwargs.get("factory_contract_addr", None) self.trading_asset_address = kwargs.get("trading_asset_address", None) diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index d9182a87..39ee76ff 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -67,7 +67,7 @@ def __init__(self, **kwargs): # Logging for debugging purpose logger.debug( - "w3: {}. chain: {}. platform: {}", self.w3, self.chain, self.platform + "w3: {}. chain: {}", self.w3, self.chain ) def initialize_platform(self): diff --git a/tests/test_unit_dexswap.py b/tests/test_unit_dexswap.py index 84349ece..82ab32ce 100755 --- a/tests/test_unit_dexswap.py +++ b/tests/test_unit_dexswap.py @@ -73,7 +73,7 @@ async def test_dextrader(dex): assert callable(dex.get_info) assert callable(dex.get_balances) assert callable(dex.get_positions) - assert callable(dex.get_pnl) + assert callable(dex.get_pnls) assert callable(dex.submit_order) for dx in dex.clients: From 2acb7a241f0b15f3e9d26625ed4f873389c27d44 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:00:18 +0200 Subject: [PATCH 12/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/contract_utils.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 39ee76ff..8f78dc3c 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -5,6 +5,7 @@ from datetime import datetime +import requests from loguru import logger from pycoingecko import CoinGeckoAPI @@ -276,10 +277,23 @@ async def get_cg_data(self, token): str or None: The data for the token on the specified platform, or None if the token is not found or an error occurs. """ + # try: + self.initialize_platform() try: - self.initialize_platform() - coin_dict = self.cg.get_coin_by_id(token.upper()) - return coin_dict.get("detail_platforms", {}).get(self.platform) + if self.platform is None: + return None + search_results = self.cg.search(query=token) + search_dict = search_results["coins"] + # logger.debug("Coingecko search results: {}", search_dict) + filtered_dict = [x for x in search_dict if x["symbol"] == token.upper()] + api_dict = [sub["api_symbol"] for sub in filtered_dict] + for i in api_dict: + coin_dict = self.cg.get_coin_by_id(i) + try: + if coin_dict["detail_platforms"][f"{self.platform}"]: + return coin_dict["detail_platforms"][f"{self.platform}"] + except (KeyError, requests.exceptions.HTTPError): + pass except Exception as e: logger.error("get_cg_data {}", e) From de03ca9c6866e8682b46265b903b4d2d2a619d4c Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:11:21 +0200 Subject: [PATCH 13/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 55 +++++++++++++++++++------------------ dxsp/handler/uniswap.py | 3 +- dxsp/utils/account_utils.py | 18 ++++++------ dxsp/utils/token_utils.py | 19 ++++++------- 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 450d6abe..08717c91 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -73,38 +73,39 @@ def __init__(self, **kwargs): Returns: None """ - self.name = kwargs.get("name", None) + get = kwargs.get + self.name = get("name", None) logger.debug(f"Setting up: {self.name}") - self.protocol = kwargs.get("library") or kwargs.get("protocol") or "uniswap" - self.protocol_version = kwargs.get("protocol_version", 2) - self.api_endpoint = kwargs.get("api_endpoint", None) - self.api_key = kwargs.get("api_key", None) - self.rpc = kwargs.get("rpc", None) - self.w3 = kwargs.get("w3", None) - self.wallet_address = kwargs.get("wallet_address", None) - self.private_key = kwargs.get("private_key", None) - self.headers = kwargs.get("headers", "{User-Agent= 'Mozilla/5.0'}") - self.abi_url = kwargs.get( + self.protocol = get("library") or get("protocol") or "uniswap" + self.protocol_version = get("protocol_version", 2) + self.api_endpoint = get("api_endpoint", None) + self.api_key = get("api_key", None) + self.rpc = get("rpc", None) + self.w3 = get("w3", None) + self.wallet_address = get("wallet_address", None) + self.private_key = get("private_key", None) + self.headers = get("headers", "{User-Agent= 'Mozilla/5.0'}") + self.abi_url = get( "abi_url", "https://raw.githubusercontent.com/Uniswap/interface/44c355c7f0f8ab5bdb3e0790560e84e59f5666f7/src/abis/erc20.json", ) - self.token_mainnet_list = kwargs.get("token_mainnet_list", None) - self.token_testnet_list = kwargs.get("token_testnet_list", None) - self.token_personal_list = kwargs.get("token_personal_list", None) - self.router_contract_addr = kwargs.get("router_contract_addr", None) - self.factory_contract_addr = kwargs.get("factory_contract_addr", None) - self.trading_asset_address = kwargs.get("trading_asset_address", None) - self.trading_risk_percentage = kwargs.get("trading_risk_percentage", None) - self.trading_asset_separator = kwargs.get("trading_asset_separator", None) - self.trading_risk_amount = kwargs.get("trading_risk_amount", None) - self.trading_slippage = kwargs.get("trading_slippage", None) - self.trading_amount_threshold = kwargs.get("trading_amount_threshold", None) - self.block_explorer_url = kwargs.get("block_explorer_url", None) - self.block_explorer_api = kwargs.get("block_explorer_api", None) - self.mapping = kwargs.get("mapping", None) - self.is_pnl_active = kwargs.get("is_pnl_active", False) - self.rotki_report_endpoint = kwargs.get("rotki_report_endpoint", None) + self.token_mainnet_list = get("token_mainnet_list", None) + self.token_testnet_list = get("token_testnet_list", None) + self.token_personal_list = get("token_personal_list", None) + self.router_contract_addr = get("router_contract_addr", None) + self.factory_contract_addr = get("factory_contract_addr", None) + self.trading_asset_address = get("trading_asset_address", None) + self.trading_risk_percentage = get("trading_risk_percentage", None) + self.trading_asset_separator = get("trading_asset_separator", None) + self.trading_risk_amount = get("trading_risk_amount", None) + self.trading_slippage = get("trading_slippage", None) + self.trading_amount_threshold = get("trading_amount_threshold", None) + self.block_explorer_url = get("block_explorer_url", None) + self.block_explorer_api = get("block_explorer_api", None) + self.mapping = get("mapping", None) + self.is_pnl_active = get("is_pnl_active", False) + self.rotki_report_endpoint = get("rotki_report_endpoint", None) if self.rpc: try: self.w3 = Web3(Web3.HTTPProvider(self.rpc)) diff --git a/dxsp/handler/uniswap.py b/dxsp/handler/uniswap.py index dacabc38..d3a6b8a1 100644 --- a/dxsp/handler/uniswap.py +++ b/dxsp/handler/uniswap.py @@ -42,9 +42,8 @@ def build_client(self): """ try: logger.debug( - "Uniswap client starting. RPC: {}, Wallet: {}", + "Uniswap client starting. RPC: {}", self.rpc, - self.wallet_address, ) self.client = Uniswap( address=self.wallet_address, diff --git a/dxsp/utils/account_utils.py b/dxsp/utils/account_utils.py index 1a4f9871..20226889 100644 --- a/dxsp/utils/account_utils.py +++ b/dxsp/utils/account_utils.py @@ -30,21 +30,23 @@ class AccountUtils: """ def __init__(self, **kwargs): - self.w3 = self.w3 = kwargs.get("w3", None) + get = kwargs.get + + self.w3 = self.w3 = get("w3", None) self.wallet_address = self.w3.to_checksum_address( - kwargs.get("wallet_address", None) + get("wallet_address", None) ) self.account_number = ( f"{int(self.w3.net.version, 16)} - " f"{str(self.wallet_address)[-8:]}" ) - self.private_key = kwargs.get("private_key", None) + self.private_key = get("private_key", None) self.trading_asset_address = self.w3.to_checksum_address( - kwargs.get("trading_asset_address", None) + get("trading_asset_address", None) ) - self.router_contract_addr = kwargs.get("router_contract_addr", None) - self.contract_utils = kwargs.get("contract_utils", None) - self.block_explorer_url = kwargs.get("block_explorer_url", None) - self.block_explorer_api = kwargs.get("block_explorer_api", None) + self.router_contract_addr = get("router_contract_addr", None) + self.contract_utils = get("contract_utils", None) + self.block_explorer_url = get("block_explorer_url", None) + self.block_explorer_api = get("block_explorer_api", None) async def get_account_balance(self) -> str: """ diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py index 1943290d..dfe7456b 100644 --- a/dxsp/utils/token_utils.py +++ b/dxsp/utils/token_utils.py @@ -3,7 +3,6 @@ 🪙 TOKEN """ - from loguru import logger from dxsp.utils.utils import fetch_url @@ -57,15 +56,15 @@ def __init__(self, **kwargs): :type symbol: str """ try: - - # logger.debug("token init {} {}", kwargs, type(kwargs)) - self.w3 = kwargs.get("w3", None) - self.address = self.w3.to_checksum_address(kwargs.get("address", None)) - self.symbol = kwargs.get("symbol", None) - self.headers = kwargs.get("headers", None) - self.abi_url = kwargs.get("abi_url", None) - self.block_explorer_url = kwargs.get("block_explorer_url", None) - self.block_explorer_api = kwargs.get("block_explorer_api", None) + get = kwargs.get + logger.debug("initializing token") + self.w3 = get("w3", None) + self.address = self.w3.to_checksum_address(get("address", None)) + self.symbol = get("symbol", None) + self.headers = get("headers", None) + self.abi_url = get("abi_url", None) + self.block_explorer_url = get("block_explorer_url", None) + self.block_explorer_api = get("block_explorer_api", None) self.decimals = None self.name = None logger.debug( From 778d787c92e3bc80247a6b9fd7a0535b49022645 Mon Sep 17 00:00:00 2001 From: mraniki Date: Sat, 13 Jul 2024 22:37:07 +0200 Subject: [PATCH 14/34] Add test_resolve_symbol function to test resolving token symbol. --- tests/test_unit_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 4367f8d7..589e9de6 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -43,6 +43,11 @@ async def test_resolve_symbol(dex_client): assert result.address == "0x514910771AF9Ca656af840dff83E8264EcF986CA" +@pytest.mark.asyncio +async def test_resolve_symbol(dex_client): + result = await dex_client.resolve_token(symbol="PEPE") + assert result.address == "0x6982508145454ce325ddbe47a25d4ec3d2311933 + @pytest.mark.asyncio async def test_get_order_amount(dex_client): sell_token = AsyncMock() From 13518bcfcd5e0820918820a2d93802a2a834b02a Mon Sep 17 00:00:00 2001 From: mraniki Date: Sat, 13 Jul 2024 22:48:35 +0200 Subject: [PATCH 15/34] :rotating_light: Fix missing double quote in test_resolve_symbol function. --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 589e9de6..2f39dba2 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -46,7 +46,7 @@ async def test_resolve_symbol(dex_client): @pytest.mark.asyncio async def test_resolve_symbol(dex_client): result = await dex_client.resolve_token(symbol="PEPE") - assert result.address == "0x6982508145454ce325ddbe47a25d4ec3d2311933 + assert result.address == "0x6982508145454ce325ddbe47a25d4ec3d2311933" @pytest.mark.asyncio async def test_get_order_amount(dex_client): From 235ea3a89f577232fd63c508b6132be92d6fb087 Mon Sep 17 00:00:00 2001 From: mraniki Date: Sat, 13 Jul 2024 22:55:05 +0200 Subject: [PATCH 16/34] Update test_unit_client.py with new resolve symbol test function --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 2f39dba2..11d273e2 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -44,7 +44,7 @@ async def test_resolve_symbol(dex_client): @pytest.mark.asyncio -async def test_resolve_symbol(dex_client): +async def test_resolve_symbol2(dex_client): result = await dex_client.resolve_token(symbol="PEPE") assert result.address == "0x6982508145454ce325ddbe47a25d4ec3d2311933" From 4b2f4f6c600ac779a756abe6fcb10f0b73aa7d0f Mon Sep 17 00:00:00 2001 From: mraniki Date: Sun, 14 Jul 2024 06:26:50 +0200 Subject: [PATCH 17/34] Update token symbol case sensitivity test assertion. --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 11d273e2..08a929c7 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -46,7 +46,7 @@ async def test_resolve_symbol(dex_client): @pytest.mark.asyncio async def test_resolve_symbol2(dex_client): result = await dex_client.resolve_token(symbol="PEPE") - assert result.address == "0x6982508145454ce325ddbe47a25d4ec3d2311933" + assert result.address == "0x6982508145454Ce325dDbE47a25d4ec3d2311933" @pytest.mark.asyncio async def test_get_order_amount(dex_client): From a3f47f8ad59d5e73d094f5a1ed6fed585552bc7f Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 11:29:06 +0200 Subject: [PATCH 18/34] =?UTF-8?q?=E2=9C=85=20Unit=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 51 +++++++++++++++---- dxsp/utils/token_utils.py | 5 +- tests/test_unit_client.py | 96 ++++++++++++++++++++++++----------- tests/test_unit_dexswap.py | 19 +------ tests/test_unit_exception.py | 1 - tests/test_unit_exception_old | 40 --------------- 6 files changed, 109 insertions(+), 103 deletions(-) delete mode 100755 tests/test_unit_exception_old diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 08717c91..0d4f5598 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -425,23 +425,54 @@ async def calculate_pnl(self, period=None): logger.error(f"Received non-200 status code: {response.status}") return 0 data = await response.json() - result = data.get("result", {}) - entries = result.get("entries", []) - # Initialize a dictionary to hold the sum of 'free' values free_values = { "trade": 0, "transaction event": 0, "fee": 0, "asset movement": 0, } + entries = data.get("result", {}).get("entries", []) for entry in entries: overview = entry.get("overview", {}) - for category, amounts in overview.items(): - try: - free_amount = float(amounts.get("free", "0")) - # Add it to the total - free_values[category] += free_amount - except ValueError: - logger.error(f"Invalid free amount: {amounts.get('free')}") + free_values.update( + { + category: free_values[category] + + float(amounts.get("free", 0)) + for category, amounts in overview.items() + } + ) return free_values + + # if self.rotki_report_endpoint is None: + # return 0 + # params = {"period": period} if period else {} + + # async with aiohttp.ClientSession() as session: + # async with session.get( + # self.rotki_report_endpoint, params=params + # ) as response: + # if response.status != 200: + # logger.error(f"Received non-200 status code: {response.status}") + # return 0 + # data = await response.json() + # result = data.get("result", {}) + # entries = result.get("entries", []) + # # Initialize a dictionary to hold the sum of 'free' values + # free_values = { + # "trade": 0, + # "transaction event": 0, + # "fee": 0, + # "asset movement": 0, + # } + # for entry in entries: + # overview = entry.get("overview", {}) + # for category, amounts in overview.items(): + # try: + # free_amount = float(amounts.get("free", "0")) + # # Add it to the total + # free_values[category] += free_amount + # except ValueError: + # logger.error(f"Invalid free amount: {amounts.get('free')}") + + # return free_values diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py index dfe7456b..f8445780 100644 --- a/dxsp/utils/token_utils.py +++ b/dxsp/utils/token_utils.py @@ -161,13 +161,10 @@ async def get_token_balance(self, wallet_address): None """ contract = await self.get_token_contract() - if contract is None or contract.functions is None: + if not contract or contract.functions is None: logger.warning("No Balance") return 0 balance = contract.functions.balanceOf(wallet_address).call() - if balance is None: - logger.warning("No Balance") - return 0 return round(self.w3.from_wei(balance, "ether"), 5) or 0 async def get_token_symbol(self): diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 08a929c7..c7e0acbb 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -20,8 +20,6 @@ def DexSwap_fixture(): return DexSwap() - - @pytest.fixture(name="dex_client") def client_fixture(dex): for dx in dex.clients: @@ -29,8 +27,15 @@ def client_fixture(dex): return dx +@pytest.fixture(name="dex_client_zero_x") +def client_zero_x_fixture(dex): + for dx in dex.clients: + if dx.protocol == "zerox": + return dx + + @pytest.mark.asyncio -async def test_resolve_address(dex_client): +async def test_resolve_token_address(dex_client): result = await dex_client.resolve_token( address="0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" ) @@ -38,16 +43,29 @@ async def test_resolve_address(dex_client): @pytest.mark.asyncio -async def test_resolve_symbol(dex_client): +async def test_resolve_token_symbol(dex_client): result = await dex_client.resolve_token(symbol="LINK") assert result.address == "0x514910771AF9Ca656af840dff83E8264EcF986CA" @pytest.mark.asyncio -async def test_resolve_symbol2(dex_client): +async def test_resolve_token_symbol2(dex_client): result = await dex_client.resolve_token(symbol="PEPE") assert result.address == "0x6982508145454Ce325dDbE47a25d4ec3d2311933" + +@pytest.mark.asyncio +async def test_resolve_token_error(dex_client): + with pytest.raises(ValueError): + await dex_client.resolve_token() + + +@pytest.mark.asyncio +async def test_token_decimals(dex_client): + result = await dex_client.resolve_token(symbol="PEPE") + assert result.decimals == "18" + + @pytest.mark.asyncio async def test_get_order_amount(dex_client): sell_token = AsyncMock() @@ -74,6 +92,18 @@ async def test_get_order_amount(dex_client): assert result == 0 +@pytest.mark.asyncio +async def test_get_quote_zero_x(dex_client_zero_x): + + result = await dex_client_zero_x.get_quote( + buy_address="0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", # USDT + sell_address="0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6", # WBTC + amount=1, + ) + assert result is not None + assert result > 0 + + @pytest.mark.asyncio async def test_get_swap(dex_client): dex_client.get_order_amount = AsyncMock(return_value="1") @@ -95,13 +125,13 @@ async def test_get_swap(dex_client): assert dex_client.account.get_confirmation.awaited -# @pytest.mark.asyncio -# async def test_get_swap_1(dex_client): -# result = await dex_client.get_swap( -# sell_token="USDT", -# buy_token="WBTC",quantity=1 -# ) -# assert result is not None +@pytest.mark.asyncio +async def test_get_swap_1(dex_client): + result = await dex_client.get_swap( + sell_token="USDT", + buy_token="WBTC",quantity=1 + ) + assert result is not None @pytest.mark.asyncio @@ -112,25 +142,31 @@ async def test_get_trading_asset_balance(dex_client): assert dex_client.account.get_trading_asset_balance.awaited -# @pytest.mark.asyncio -# async def test_get_account_open_positions(dex_client): -# dex_client.account.get_account_open_positions = AsyncMock() -# result = await dex_client.get_account_open_positions() -# assert result is not None -# assert dex_client.account.get_account_open_positions.awaited +@pytest.mark.asyncio +async def test_get_account_open_positions(dex_client): + dex_client.account.get_account_open_positions = AsyncMock() + result = await dex_client.get_account_open_positions() + assert result is not None + assert dex_client.account.get_account_open_positions.awaited + + +@pytest.mark.asyncio +async def test_get_account_margin(dex_client): + dex_client.account.get_account_margin = AsyncMock() + result = await dex_client.get_account_margin() + assert result is not None + assert dex_client.account.get_account_margin.awaited -# @pytest.mark.asyncio -# async def test_get_account_margin(dex_client): -# dex_client.account.get_account_margin = AsyncMock() -# result = await dex_client.get_account_margin() -# assert result is not None -# assert dex_client.account.get_account_margin.awaited +@pytest.mark.asyncio +async def test_get_account_pnl(dex_client): + dex_client.account.get_account_pnl = AsyncMock() + result = await dex_client.get_account_pnl() + assert result is not None + assert dex_client.account.get_account_pnl.awaited -# @pytest.mark.asyncio -# async def test_get_account_pnl(dex_client): -# dex_client.account.get_account_pnl = AsyncMock() -# result = await dex_client.get_account_pnl() -# assert result is not None -# assert dex_client.account.get_account_pnl.awaited +@pytest.mark.asyncio +async def test_calculate_pnl_rotki_report_endpoint_none(dex_client): + result = await dex_client.calculate_pnl() + assert result == 0 diff --git a/tests/test_unit_dexswap.py b/tests/test_unit_dexswap.py index 82ab32ce..24a7d8a2 100755 --- a/tests/test_unit_dexswap.py +++ b/tests/test_unit_dexswap.py @@ -13,7 +13,7 @@ @pytest.fixture(scope="session", autouse=True) def set_test_settings(): settings.configure(FORCE_ENV_FOR_DYNACONF="dxsp") - + @pytest.fixture(name="dex") def DexSwap_fixture(): @@ -168,20 +168,3 @@ async def test_submit_invalid_symbol(dex, invalid_symbol): assert result is not None assert "⚠️" in result - -# @pytest.mark.asyncio -# async def test_submit_order_invalid(dex, invalid_order): -# result = await dex.submit_order(invalid_order) -# assert "⚠️" in result - - -@pytest.mark.asyncio -async def test_get_quote_zero_x(dex_client_zero_x): - - result = await dex_client_zero_x.get_quote( - buy_address="0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", # USDT - sell_address="0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6", # WBTC - amount=1, - ) - assert result is not None - assert result > 0 diff --git a/tests/test_unit_exception.py b/tests/test_unit_exception.py index 6a196022..8b22727a 100644 --- a/tests/test_unit_exception.py +++ b/tests/test_unit_exception.py @@ -24,7 +24,6 @@ async def test_module_exception(caplog): ) -# @pytest.mark.asyncio async def test_create_client_exception(caplog): settings.dxsp_enabled = True test_class = DexSwap() diff --git a/tests/test_unit_exception_old b/tests/test_unit_exception_old deleted file mode 100755 index aadea2f5..00000000 --- a/tests/test_unit_exception_old +++ /dev/null @@ -1,40 +0,0 @@ -# """ -# DEXSWAP Unit Test -# """ - -# import pytest - -# from dxsp import DexSwap -# from dxsp.config import settings -# #from dxsp.handler.uniswap import UniswapHandler - - -# @pytest.fixture(scope="session", autouse=True) -# def set_test_settings(): -# settings.configure(FORCE_ENV_FOR_DYNACONF="exception") - - -# # @pytest.fixture(name="dex") -# # def DexSwap_fixture(): -# # return DexSwap() - - -# def test_dynaconf_is_in_exception(): -# print(settings.VALUE) -# assert settings.VALUE == "exception" - - - -# @pytest.mark.asyncio -# async def test_moduledisabled(caplog): -# """Init Testing""" -# DexSwap() -# print(settings.dxsp_enabled) -# assert "Loaded 0 clients" in caplog.text - - -# # @pytest.mark.asyncio -# # async def test_uniswap_exception(dex, caplog): -# # """Init Testing""" -# # UniswapHandler() -# # assert "Loaded 0 clients" in caplog.text From d409b397e1b529545eec52b2e1afe0c1a8166d2e Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:18:31 +0200 Subject: [PATCH 19/34] =?UTF-8?q?=F0=9F=9A=A8=20=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 52 +++++++++--------------------------------- 1 file changed, 11 insertions(+), 41 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 0d4f5598..060db9cb 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -413,6 +413,7 @@ async def calculate_pnl(self, period=None): Returns: pnl: The calculated PnL value. """ + if self.rotki_report_endpoint is None: return 0 params = {"period": period} if period else {} @@ -425,54 +426,23 @@ async def calculate_pnl(self, period=None): logger.error(f"Received non-200 status code: {response.status}") return 0 data = await response.json() + result = data.get("result", {}) + entries = result.get("entries", []) + # Initialize a dictionary to hold the sum of 'free' values free_values = { "trade": 0, "transaction event": 0, "fee": 0, "asset movement": 0, } - entries = data.get("result", {}).get("entries", []) for entry in entries: overview = entry.get("overview", {}) - free_values.update( - { - category: free_values[category] - + float(amounts.get("free", 0)) - for category, amounts in overview.items() - } - ) + for category, amounts in overview.items(): + try: + free_amount = float(amounts.get("free", "0")) + # Add it to the total + free_values[category] += free_amount + except ValueError: + logger.error(f"Invalid free amount: {amounts.get('free')}") return free_values - - # if self.rotki_report_endpoint is None: - # return 0 - # params = {"period": period} if period else {} - - # async with aiohttp.ClientSession() as session: - # async with session.get( - # self.rotki_report_endpoint, params=params - # ) as response: - # if response.status != 200: - # logger.error(f"Received non-200 status code: {response.status}") - # return 0 - # data = await response.json() - # result = data.get("result", {}) - # entries = result.get("entries", []) - # # Initialize a dictionary to hold the sum of 'free' values - # free_values = { - # "trade": 0, - # "transaction event": 0, - # "fee": 0, - # "asset movement": 0, - # } - # for entry in entries: - # overview = entry.get("overview", {}) - # for category, amounts in overview.items(): - # try: - # free_amount = float(amounts.get("free", "0")) - # # Add it to the total - # free_values[category] += free_amount - # except ValueError: - # logger.error(f"Invalid free amount: {amounts.get('free')}") - - # return free_values From e30a62a1fed4f049c141a3d5fd187632231c8557 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:24:37 +0200 Subject: [PATCH 20/34] =?UTF-8?q?=E2=9C=85=20Unit=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index c7e0acbb..f3573b79 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -63,7 +63,7 @@ async def test_resolve_token_error(dex_client): @pytest.mark.asyncio async def test_token_decimals(dex_client): result = await dex_client.resolve_token(symbol="PEPE") - assert result.decimals == "18" + assert result.decimals == 18 @pytest.mark.asyncio From fa8ecd9dc0697d41b99b74a90d36292dcf77f916 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:39:49 +0200 Subject: [PATCH 21/34] =?UTF-8?q?=E2=9C=85=20Unit=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_unit_client.py | 8 +++----- tests/test_unit_dexswap.py | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index f3573b79..b302be24 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -52,6 +52,8 @@ async def test_resolve_token_symbol(dex_client): async def test_resolve_token_symbol2(dex_client): result = await dex_client.resolve_token(symbol="PEPE") assert result.address == "0x6982508145454Ce325dDbE47a25d4ec3d2311933" + assert result.decimals == 18 + assert result.get_contract_function is not None @pytest.mark.asyncio @@ -63,7 +65,6 @@ async def test_resolve_token_error(dex_client): @pytest.mark.asyncio async def test_token_decimals(dex_client): result = await dex_client.resolve_token(symbol="PEPE") - assert result.decimals == 18 @pytest.mark.asyncio @@ -127,10 +128,7 @@ async def test_get_swap(dex_client): @pytest.mark.asyncio async def test_get_swap_1(dex_client): - result = await dex_client.get_swap( - sell_token="USDT", - buy_token="WBTC",quantity=1 - ) + result = await dex_client.get_swap(sell_token="USDT", buy_token="WBTC", quantity=1) assert result is not None diff --git a/tests/test_unit_dexswap.py b/tests/test_unit_dexswap.py index 24a7d8a2..56ab01a7 100755 --- a/tests/test_unit_dexswap.py +++ b/tests/test_unit_dexswap.py @@ -13,7 +13,7 @@ @pytest.fixture(scope="session", autouse=True) def set_test_settings(): settings.configure(FORCE_ENV_FOR_DYNACONF="dxsp") - + @pytest.fixture(name="dex") def DexSwap_fixture(): @@ -167,4 +167,3 @@ async def test_submit_invalid_symbol(dex, invalid_symbol): result = await dex.submit_order(invalid_symbol) assert result is not None assert "⚠️" in result - From 63514ee3cc1ba60a565fc3b68836bdb798bcaa3c Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:18:57 +0200 Subject: [PATCH 22/34] =?UTF-8?q?=F0=9F=9A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_unit_client.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index b302be24..1a8302cf 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -49,7 +49,7 @@ async def test_resolve_token_symbol(dex_client): @pytest.mark.asyncio -async def test_resolve_token_symbol2(dex_client): +async def test_resolve_token(dex_client): result = await dex_client.resolve_token(symbol="PEPE") assert result.address == "0x6982508145454Ce325dDbE47a25d4ec3d2311933" assert result.decimals == 18 @@ -62,10 +62,6 @@ async def test_resolve_token_error(dex_client): await dex_client.resolve_token() -@pytest.mark.asyncio -async def test_token_decimals(dex_client): - result = await dex_client.resolve_token(symbol="PEPE") - @pytest.mark.asyncio async def test_get_order_amount(dex_client): From 98de87a4bc1e08d6841f5c4639c5161c954452a2 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:37:11 +0200 Subject: [PATCH 23/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/token_utils.py | 26 +++++++++++++------------- tests/test_unit_client.py | 3 ++- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dxsp/utils/token_utils.py b/dxsp/utils/token_utils.py index f8445780..c1fa4ec5 100644 --- a/dxsp/utils/token_utils.py +++ b/dxsp/utils/token_utils.py @@ -132,19 +132,19 @@ async def get_token_contract(self): contract = self.w3.eth.contract(address=self.address, abi=self.abi) return contract - def get_contract_function(self, contract, func_name: str): - """ - Get the contract function by name. - - Args: - contract: The contract object. - func_name (str): The name of the function. - - Returns: - bool: True if the function exists - in the contract, False otherwise. - """ - return func_name in dir(contract.functions) + # def get_contract_function(self, contract, func_name: str): + # """ + # Get the contract function by name. + + # Args: + # contract: The contract object. + # func_name (str): The name of the function. + + # Returns: + # bool: True if the function exists + # in the contract, False otherwise. + # """ + # return func_name in dir(contract.functions) async def get_token_balance(self, wallet_address): """ diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 1a8302cf..a1590fe3 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -53,7 +53,8 @@ async def test_resolve_token(dex_client): result = await dex_client.resolve_token(symbol="PEPE") assert result.address == "0x6982508145454Ce325dDbE47a25d4ec3d2311933" assert result.decimals == 18 - assert result.get_contract_function is not None + # assert result.get_contract_function is not None + # balanceOf = result.get_contract_function("balanceOf") @pytest.mark.asyncio From 1d80c44d87028f8d6682058ea9958e60b037dc80 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:56:00 +0200 Subject: [PATCH 24/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/main.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/dxsp/main.py b/dxsp/main.py index bf5ec34a..d7b5699d 100644 --- a/dxsp/main.py +++ b/dxsp/main.py @@ -102,14 +102,13 @@ def _create_client(self, **kwargs): This function takes in a dictionary of keyword arguments, `kwargs`, containing the necessary information to create a client. The required key in `kwargs` is "library", which specifies the protocol to use for - communication with the LLM. The value of "library" must match one of the - libraries supported by MyLLM. + communication with the Dex. The value of "library" must match one of the + libraries supported by DXSP. This function retrieves the class used to create the client based on the value of "library" from the mapping of library names to client classes stored in `self.client_classes`. If the value of "library" does not - match any of the libraries supported, the function logs an error message - and returns None. + match any of the libraries supported, the function returns None. If the class used to create the client is found, the function creates a new instance of the class using the keyword arguments in `kwargs` and @@ -128,15 +127,10 @@ def _create_client(self, **kwargs): library is not supported. """ - library = ( - kwargs.get("library") - or kwargs.get("platform") - or kwargs.get("protocol") - or kwargs.get("parser_library") - or "uniswap" + library = kwargs.get("library") or kwargs.get("platform") or "uniswap" + return self.client_classes.get(f"{library.capitalize()}Handler", None).__call__( + **kwargs ) - cls = self.client_classes.get((f"{library.capitalize()}Handler")) - return None if cls is None else cls(**kwargs) def get_all_client_classes(self): """ From e7a1948809cdbecfa22f1a17400f6295039c739e Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:14:07 +0200 Subject: [PATCH 25/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/kwenta.py | 27 +++++++-------------------- dxsp/handler/uniswap.py | 6 ++---- dxsp/handler/zerox.py | 6 ++---- dxsp/main.py | 2 +- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/dxsp/handler/kwenta.py b/dxsp/handler/kwenta.py index 60b4e0ec..ac53237c 100644 --- a/dxsp/handler/kwenta.py +++ b/dxsp/handler/kwenta.py @@ -59,39 +59,26 @@ async def get_quote( Exception: If an error occurs during the retrieval process. """ try: - logger.debug( - "Kwenta get_quote {} {} {} {}", - buy_address, - buy_symbol, - sell_address, - sell_symbol, - ) - # Resolve buy_token buy_token = await self.resolve_token( address_or_symbol=buy_address or buy_symbol or self.trading_asset_address ) - - # Resolve sell_token sell_token = await self.resolve_token( address_or_symbol=sell_address or sell_symbol ) - logger.info("buy_token: {}", buy_token) - logger.info("sell_token: {}", sell_token) - if not buy_token: - return "Buy token not found" - if not sell_token: - return "Sell token not found" + + if not buy_token or not sell_token: + return "⚠️ Buy or sell token not found" + market = self.client.markets[f"{sell_token.symbol}"] logger.info("market: {}", market) - quote = self.client.get_current_asset_price(sell_token.symbol) logger.info("quote: {}", quote) - return quote + return quote or "⚠️ Quote failed" except Exception as error: - logger.error("Kwenta Quote failed {}, Verify Kwenta wallet setup", error) - logger.debug(error) + logger.error("Quote failed {}", error) + return f"⚠️ {error}" async def make_swap(self, sell_address, buy_address, amount): pass diff --git a/dxsp/handler/uniswap.py b/dxsp/handler/uniswap.py index d3a6b8a1..1f7b8037 100644 --- a/dxsp/handler/uniswap.py +++ b/dxsp/handler/uniswap.py @@ -101,10 +101,8 @@ async def get_quote( ) logger.debug("Buy token {}. Sell token {}", buy_token, sell_token) - if not buy_token: - return "Buy token not found" - if not sell_token: - return "Sell token not found" + if not buy_token or not sell_token: + return "⚠️ Buy or sell token not found" amount_wei = amount * (10 ** (sell_token.decimals)) logger.debug( diff --git a/dxsp/handler/zerox.py b/dxsp/handler/zerox.py index 5c9e3ca7..82e01e02 100644 --- a/dxsp/handler/zerox.py +++ b/dxsp/handler/zerox.py @@ -67,10 +67,8 @@ async def get_quote( sell_token = await self.resolve_token( address_or_symbol=sell_address or sell_symbol ) - if not buy_token: - return "Buy token not found" - if not sell_token: - return "Sell token not found" + if not buy_token or not sell_token: + return "⚠️ Buy or sell token not found" amount_wei = amount * (10 ** (sell_token.decimals)) url = ( diff --git a/dxsp/main.py b/dxsp/main.py index d7b5699d..f11fbbe2 100644 --- a/dxsp/main.py +++ b/dxsp/main.py @@ -127,7 +127,7 @@ def _create_client(self, **kwargs): library is not supported. """ - library = kwargs.get("library") or kwargs.get("platform") or "uniswap" + library = kwargs.get("library") or kwargs.get("protocol") or "uniswap" return self.client_classes.get(f"{library.capitalize()}Handler", None).__call__( **kwargs ) From 0c534ae2c38d82e5e9cb9dbe7e0abf19cb991a64 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:36:57 +0200 Subject: [PATCH 26/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/utils/contract_utils.py | 2 +- tests/test_unit_dexswap.py | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/dxsp/utils/contract_utils.py b/dxsp/utils/contract_utils.py index 8f78dc3c..42f95540 100644 --- a/dxsp/utils/contract_utils.py +++ b/dxsp/utils/contract_utils.py @@ -172,7 +172,7 @@ async def search(self, token): token_instance = await self.search_cg_data(token) if token_instance is None: - raise Exception(f"Token not found: {token} on {self.chain}") + raise Exception(f"Token {token} not found on {self.chain}") token_instance.block_explorer_api = self.block_explorer_api token_instance.block_explorer_url = self.block_explorer_url return token_instance diff --git a/tests/test_unit_dexswap.py b/tests/test_unit_dexswap.py index 56ab01a7..4a251f19 100755 --- a/tests/test_unit_dexswap.py +++ b/tests/test_unit_dexswap.py @@ -53,13 +53,6 @@ def invalid_symbol_fixture(): "quantity": 1, } - -# @pytest.fixture(name="invalid_order") -# def invalid_order_fixture(): -# """Return order parameters.""" -# return "not an order" - - def test_dynaconf_is_in_testing(): print(settings.VALUE) assert settings.VALUE == "On Testing" @@ -150,8 +143,8 @@ async def test_get_quotes(dex): async def test_get_quotes_invalid(dex): """getquote Testing""" result = await dex.get_quotes(symbol="NOTATOKEN") - assert "None" in result - + assert "⚠️ Token" in result + assert "not found" in result @pytest.mark.asyncio async def test_submit_order(dex, order): From 7d7f85bdd47aa1ce96dddc0e2d82b0ef8f3ce4fe Mon Sep 17 00:00:00 2001 From: mraniki Date: Sun, 14 Jul 2024 14:57:15 +0200 Subject: [PATCH 27/34] Add new test for resolving token symbol with BSC network. --- tests/test_unit_client.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index a1590fe3..f5597a64 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -26,6 +26,11 @@ def client_fixture(dex): if dx.name == "eth": return dx +@pytest.fixture(name="dex_client_bsc") +def client_fixture(dex): + for dx in dex.clients: + if dx.name == "bsc": + return dx @pytest.fixture(name="dex_client_zero_x") def client_zero_x_fixture(dex): @@ -42,10 +47,15 @@ async def test_resolve_token_address(dex_client): assert result.symbol == "WBTC" +# @pytest.mark.asyncio +# async def test_resolve_token_symbol(dex_client): +# result = await dex_client.resolve_token(symbol="LINK") +# assert result.address == "0x514910771AF9Ca656af840dff83E8264EcF986CA" + @pytest.mark.asyncio -async def test_resolve_token_symbol(dex_client): - result = await dex_client.resolve_token(symbol="LINK") - assert result.address == "0x514910771AF9Ca656af840dff83E8264EcF986CA" +async def test_resolve_token_symbol(dex_client_bsc): + result = await dex_client.resolve_token(symbol="TON") + assert result.address == "0x76a797a59ba2c17726896976b7b3747bfd1d220f" @pytest.mark.asyncio From d2cbc508f86eefaf58933e6eeb8491a950199b42 Mon Sep 17 00:00:00 2001 From: mraniki Date: Sun, 14 Jul 2024 15:01:34 +0200 Subject: [PATCH 28/34] Refactor resolve_token function for BSC client. --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index f5597a64..8460dfaa 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -54,7 +54,7 @@ async def test_resolve_token_address(dex_client): @pytest.mark.asyncio async def test_resolve_token_symbol(dex_client_bsc): - result = await dex_client.resolve_token(symbol="TON") + result = await dex_client_bsc.resolve_token(symbol="TON") assert result.address == "0x76a797a59ba2c17726896976b7b3747bfd1d220f" From 10f46fa1142789b8ec4f6faf20e1d218cb1e0524 Mon Sep 17 00:00:00 2001 From: mraniki Date: Sun, 14 Jul 2024 15:09:50 +0200 Subject: [PATCH 29/34] Refactor client_fixture function in test_unit_client file --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index 8460dfaa..e0216b79 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -27,7 +27,7 @@ def client_fixture(dex): return dx @pytest.fixture(name="dex_client_bsc") -def client_fixture(dex): +def client_fixture_bsc(dex): for dx in dex.clients: if dx.name == "bsc": return dx From d7ffd76cbcea3eb822c17f26882dd950b84bc7bf Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:45:28 +0200 Subject: [PATCH 30/34] =?UTF-8?q?=E2=9C=85=20Unit=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_unit_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_client.py b/tests/test_unit_client.py index e0216b79..8b1837d7 100755 --- a/tests/test_unit_client.py +++ b/tests/test_unit_client.py @@ -55,7 +55,7 @@ async def test_resolve_token_address(dex_client): @pytest.mark.asyncio async def test_resolve_token_symbol(dex_client_bsc): result = await dex_client_bsc.resolve_token(symbol="TON") - assert result.address == "0x76a797a59ba2c17726896976b7b3747bfd1d220f" + assert result.address == "0x76A797A59Ba2C17726896976B7B3747BfD1d220f" @pytest.mark.asyncio From ab9c192fd9dd686df7ec0bd78a3c7cdb8aebe41d Mon Sep 17 00:00:00 2001 From: mraniki Date: Sun, 14 Jul 2024 16:16:30 +0200 Subject: [PATCH 31/34] :white_check_mark: Unit Test --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100755 index e69de29b..00000000 From 8ea17fa12aef0cbc51cbbabab4df94d0ca53cd50 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 16:28:54 +0200 Subject: [PATCH 32/34] =?UTF-8?q?=E2=9C=85=20Unit=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 060db9cb..00b9f262 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -106,6 +106,7 @@ def __init__(self, **kwargs): self.mapping = get("mapping", None) self.is_pnl_active = get("is_pnl_active", False) self.rotki_report_endpoint = get("rotki_report_endpoint", None) + self.client = None if self.rpc: try: self.w3 = Web3(Web3.HTTPProvider(self.rpc)) @@ -144,8 +145,6 @@ def __init__(self, **kwargs): else: self.account_number = None - self.client = None - async def resolve_token(self, **kwargs): """ A function to resolve a token based on the input address or symbol. From bb5b83e21d2d85c3a2fa62754216676ca760ebe7 Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 16:54:58 +0200 Subject: [PATCH 33/34] =?UTF-8?q?=F0=9F=A5=85=20Improve=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 6 ++++-- dxsp/handler/kwenta.py | 2 ++ dxsp/handler/uniswap.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 00b9f262..6d41f7f4 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -8,6 +8,7 @@ import aiohttp from loguru import logger from web3 import Web3 +from web3.exceptions import InvalidResponse from web3.gas_strategies.time_based import medium_gas_price_strategy from web3.middleware import geth_poa_middleware @@ -115,8 +116,9 @@ def __init__(self, **kwargs): logger.debug( f"Chain {self.w3.net.version} - {int(self.w3.net.version, 16)}" ) - except Exception as e: - logger.error(f"Failed to connect to RPC: {e}") + except (InvalidResponse, Exception) as e: + logger.error(f"Invalid RPC URL or response: {e}") + self.w3 = None if self.w3 and self.wallet_address: self.chain = self.w3.net.version diff --git a/dxsp/handler/kwenta.py b/dxsp/handler/kwenta.py index ac53237c..24b7d836 100644 --- a/dxsp/handler/kwenta.py +++ b/dxsp/handler/kwenta.py @@ -27,6 +27,8 @@ def __init__( """ super().__init__(**kwargs) + if self.rpc is None or self.w3 is None: + return self.client = Kwenta( network_id=int(self.w3.net.version), provider_rpc=self.rpc, diff --git a/dxsp/handler/uniswap.py b/dxsp/handler/uniswap.py index 1f7b8037..852b3a8b 100644 --- a/dxsp/handler/uniswap.py +++ b/dxsp/handler/uniswap.py @@ -26,7 +26,7 @@ def __init__( """ super().__init__(**kwargs) - if self.rpc is None: + if self.rpc is None or self.w3 is None: return self.build_client() From a092f24004d0d63f95615281562502a82122f95e Mon Sep 17 00:00:00 2001 From: mraniki <8766259+mraniki@users.noreply.github.com> Date: Sun, 14 Jul 2024 17:19:20 +0200 Subject: [PATCH 34/34] =?UTF-8?q?=E2=9C=85=20Unit=20Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dxsp/handler/client.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dxsp/handler/client.py b/dxsp/handler/client.py index 6d41f7f4..a53d7dd5 100644 --- a/dxsp/handler/client.py +++ b/dxsp/handler/client.py @@ -8,7 +8,8 @@ import aiohttp from loguru import logger from web3 import Web3 -from web3.exceptions import InvalidResponse + +# from web3.exceptions import Web3Exception from web3.gas_strategies.time_based import medium_gas_price_strategy from web3.middleware import geth_poa_middleware @@ -108,6 +109,8 @@ def __init__(self, **kwargs): self.is_pnl_active = get("is_pnl_active", False) self.rotki_report_endpoint = get("rotki_report_endpoint", None) self.client = None + self.chain = None + self.account_number = None if self.rpc: try: self.w3 = Web3(Web3.HTTPProvider(self.rpc)) @@ -116,7 +119,8 @@ def __init__(self, **kwargs): logger.debug( f"Chain {self.w3.net.version} - {int(self.w3.net.version, 16)}" ) - except (InvalidResponse, Exception) as e: + except Exception as e: + # This block catches all other exceptions logger.error(f"Invalid RPC URL or response: {e}") self.w3 = None @@ -144,8 +148,6 @@ def __init__(self, **kwargs): block_explorer_url=self.block_explorer_url, block_explorer_api=self.block_explorer_api, ) - else: - self.account_number = None async def resolve_token(self, **kwargs): """