From f54ba536f4f660faf94976e8c18cb9e4fecb8ccb Mon Sep 17 00:00:00 2001 From: zarathustra Date: Wed, 7 May 2025 22:49:30 +0200 Subject: [PATCH 01/15] feat: Address model --- derive_client/data_types/models.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/derive_client/data_types/models.py b/derive_client/data_types/models.py index 7c5403d3..32ad08b3 100644 --- a/derive_client/data_types/models.py +++ b/derive_client/data_types/models.py @@ -12,7 +12,25 @@ from .enums import ChainID, Currency, TxStatus -Address = str +from pydantic_core import core_schema +from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler +from eth_utils import to_checksum_address, is_address + + +class Address(str): + @classmethod + def __get_pydantic_core_schema__(cls, _source, _handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.no_info_before_validator_function(cls._validate, core_schema.str_schema()) + + @classmethod + def __get_pydantic_json_schema__(cls, _schema, _handler: GetJsonSchemaHandler) -> dict: + return {"type": "string", "format": "ethereum-address"} + + @classmethod + def _validate(cls, v: str) -> str: + if not is_address(v): + raise ValueError(f"Invalid Ethereum address: {v}") + return to_checksum_address(v) @dataclass From 87582617e4e83f9fe2a3e86e2c72ad10a2c4c1ba Mon Sep 17 00:00:00 2001 From: zarathustra Date: Wed, 7 May 2025 22:50:32 +0200 Subject: [PATCH 02/15] feat: validate_arguments on BaseClient.__init__ --- derive_client/clients/base_client.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index 5867e583..ef3c962b 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -5,9 +5,11 @@ import random from decimal import Decimal from time import sleep +from logging import Logger import eth_abi import requests +from pydantic import validate_arguments from derive_action_signing.module_data import ( DepositModuleData, RecipientTransferERC20ModuleData, @@ -64,15 +66,16 @@ def _create_signature_headers(self): session_key_or_wallet_private_key=self.signer._private_key, ) + @validate_arguments(config=dict(arbitrary_types_allowed=True)) def __init__( self, - wallet: str, + wallet: Address, private_key: str, env: Environment, - logger=None, - verbose=False, - subaccount_id=None, - referral_code=None, + logger: Logger | None = None, + verbose: bool = False, + subaccount_id: int | None = None, + referral_code: Address | None = None, ): self.verbose = verbose self.env = env @@ -191,7 +194,7 @@ def fetch_subaccounts(self): payload = {"wallet": self.wallet} return self._send_request(url, json=payload) - def fetch_subaccount(self, subaccount_id): + def fetch_subaccount(self, subaccount_id: int): """ Returns information for a given subaccount """ @@ -209,7 +212,7 @@ def _internal_map_instrument(self, instrument_type, currency): def create_order( self, price, - amount, + amount: int, instrument_name: str, reduce_only=False, instrument_type: InstrumentType = InstrumentType.PERP, @@ -504,7 +507,7 @@ def fetch_tickers( def create_subaccount( self, - amount=0, + amount: int = 0, subaccount_type: SubaccountType = SubaccountType.STANDARD, collateral_asset: CollateralAsset = CollateralAsset.USDC, underlying_currency: UnderlyingCurrency = UnderlyingCurrency.ETH, @@ -629,7 +632,7 @@ def get_mmp_config(self, subaccount_id: int, currency: UnderlyingCurrency = None def set_mmp_config( self, - subaccount_id, + subaccount_id: int, currency: UnderlyingCurrency, mmp_frozen_time: int, mmp_interval: int, @@ -775,7 +778,7 @@ def transfer_from_funding_to_subaccount(self, amount: int, asset_name: str, suba json=payload, ) - def get_manager_for_subaccount(self, subaccount_id, asset_name): + def get_manager_for_subaccount(self, subaccount_id: int, asset_name): """ Look up the manager for a subaccount From f347305071f5665a468768f1e2b5a82fe825d734 Mon Sep 17 00:00:00 2001 From: zarathustra Date: Wed, 7 May 2025 23:17:13 +0200 Subject: [PATCH 03/15] fix: BaseClient.wallet is always passed via __init__, never signer.address --- derive_client/clients/base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index ef3c962b..31d2a7e1 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -83,7 +83,7 @@ def __init__( self.logger = logger or get_logger() self.web3_client = Web3() self.signer = self.web3_client.eth.account.from_key(private_key) - self.wallet = self.signer.address if not wallet else wallet + self.wallet = wallet if subaccount_id is None: subaccount_id = self._get_first_subaccount_id() self.subaccount_id = int(subaccount_id) From 4fb172704c2c9d7866d7fe33968e4f8b24055a51 Mon Sep 17 00:00:00 2001 From: zarathustra Date: Wed, 7 May 2025 23:46:45 +0200 Subject: [PATCH 04/15] feat: verify Derive smart contract wallet address --- derive_client/clients/base_client.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index 31d2a7e1..70d263a3 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -25,7 +25,7 @@ from websocket import WebSocketConnectionClosedException, create_connection from derive_client._bridge import BridgeClient -from derive_client.constants import CONFIGS, DEFAULT_REFERER, PUBLIC_HEADERS, TARGET_SPEED, TOKEN_DECIMALS +from derive_client.constants import CONFIGS, DEFAULT_REFERER, PUBLIC_HEADERS, TARGET_SPEED, TOKEN_DECIMALS, LIGHT_ACCOUNT_ABI_PATH from derive_client.data_types import ( Address, ChainID, @@ -83,12 +83,23 @@ def __init__( self.logger = logger or get_logger() self.web3_client = Web3() self.signer = self.web3_client.eth.account.from_key(private_key) - self.wallet = wallet + self.wallet = self._verify_wallet(wallet) if subaccount_id is None: subaccount_id = self._get_first_subaccount_id() self.subaccount_id = int(subaccount_id) self.referral_code = referral_code + def _verify_wallet(self, wallet: Address) -> Address: + w3 = get_w3_connection(ChainID.DERIVE) + if not w3.eth.get_code(wallet): + raise ValueError(f"{wallet} appears to be an EOA (no bytecode). Expected a smart-contract wallet on Derive.") + abi = json.loads(LIGHT_ACCOUNT_ABI_PATH.read_text()) + contract = w3.eth.contract(address=wallet, abi=abi) + owner = contract.functions.owner().call() + if not owner == self.signer.address: + raise ValueError(f"Smart Contract wallet owner mismatch: on-chain owner={owner}, signer={self.signer.address}") + return wallet + def _get_first_subaccount_id(self) -> int: self.logger.debug("No subaccount_id provided, fetching from API…") subaccounts = self.fetch_subaccounts() From f5c2ac8c748f49a8bcb6f23ebb9a48bb8c7fac2f Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 00:01:46 +0200 Subject: [PATCH 05/15] feat: verify Derive subaccount id --- derive_client/clients/base_client.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index 70d263a3..4838f6e8 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -84,9 +84,7 @@ def __init__( self.web3_client = Web3() self.signer = self.web3_client.eth.account.from_key(private_key) self.wallet = self._verify_wallet(wallet) - if subaccount_id is None: - subaccount_id = self._get_first_subaccount_id() - self.subaccount_id = int(subaccount_id) + self.subaccount_id = self._determine_subaccount_id(subaccount_id) self.referral_code = referral_code def _verify_wallet(self, wallet: Address) -> Address: @@ -100,13 +98,13 @@ def _verify_wallet(self, wallet: Address) -> Address: raise ValueError(f"Smart Contract wallet owner mismatch: on-chain owner={owner}, signer={self.signer.address}") return wallet - def _get_first_subaccount_id(self) -> int: - self.logger.debug("No subaccount_id provided, fetching from API…") + def _determine_subaccount_id(self, subaccount_id: int | None) -> int: subaccounts = self.fetch_subaccounts() - self.logger.info(f"Subaccounts retrieved: {subaccounts!r}") if not (subaccount_ids := subaccounts.get("subaccount_ids", [])): - raise ValueError("No subaccounts found. Please create one on Derive first.") - subaccount_id = subaccount_ids[0] + raise ValueError(f"No subaccounts found for {self.wallet}. Please create one on Derive first.") + if subaccount_id is not None and subaccount_id not in subaccount_ids: + raise ValueError(f"Provided subaccount {subaccount_id} not among retrieved aubaccounts: {subaccounts!r}") + subaccount_id = subaccount_id or subaccount_ids[0] self.logger.info(f"Selected subaccount_id: {subaccount_id}") return subaccount_id From 5e5a14e7125a94fc2ea7d7232bab34e96ea59058 Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 00:09:40 +0200 Subject: [PATCH 06/15] chore: upgrade black to ^24 to align with py313 compatibility --- poetry.lock | 198 +++++++++++++------------------------------------ pyproject.toml | 2 +- 2 files changed, 53 insertions(+), 147 deletions(-) diff --git a/poetry.lock b/poetry.lock index a88483e9..e0ae8510 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -7,7 +7,6 @@ description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "aiohappyeyeballs-2.5.0-py3-none-any.whl", hash = "sha256:0850b580748c7071db98bffff6d4c94028d0d3035acc20fd721a0ce7e8cac35d"}, {file = "aiohappyeyeballs-2.5.0.tar.gz", hash = "sha256:18fde6204a76deeabc97c48bdd01d5801cfda5d6b9c8bbeb1aaaee9d648ca191"}, @@ -20,7 +19,6 @@ description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4fe27dbbeec445e6e1291e61d61eb212ee9fed6e47998b27de71d70d3e8777d"}, {file = "aiohttp-3.11.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e64ca2dbea28807f8484c13f684a2f761e69ba2640ec49dacd342763cc265ef"}, @@ -116,7 +114,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -125,7 +123,6 @@ description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -141,7 +138,6 @@ description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -167,19 +163,18 @@ description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "bitarray" @@ -188,7 +183,6 @@ description = "efficient arrays of booleans -- C extension" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "bitarray-3.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb630142c0371862e114cc92fb3c7984b72bbbc4c7e7465225402ab8c7737637"}, {file = "bitarray-3.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:737755812c8834077885c0843e80eaaa2bf289c149f40a4ad0ed79e848fcbaf3"}, @@ -328,35 +322,34 @@ files = [ [[package]] name = "black" -version = "23.12.1" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] -markers = "python_version <= \"3.11\"" -files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] @@ -370,7 +363,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -381,7 +374,6 @@ description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -394,7 +386,6 @@ description = "The Real First Universal Charset Detector. Open, modern and activ optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -497,7 +488,6 @@ description = "Python bindings for C-KZG-4844" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "ckzg-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b7f9ba6d215f8981c5545f952aac84875bd564a63da02fb22a3d1321662ecdc0"}, {file = "ckzg-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8fdec3ff96399acba9baeef9e1b0b5258c08f73245780e6c69f7b73def5e8d0a"}, @@ -602,7 +592,6 @@ description = "Build Nice User Interfaces In The Terminal" optional = false python-versions = "<4.0.0,>=3.8.1" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "cli_ui-0.18.0-py3-none-any.whl", hash = "sha256:8d9484586d8eaba9f94aebaa12aa876fabdf1a3a50bdca113b2cb739eeaf78fa"}, {file = "cli_ui-0.18.0.tar.gz", hash = "sha256:3e6c80ada5b4b09c6701ca93daf31df8b70486c64348d1fc7f3288ef3bd0479c"}, @@ -620,7 +609,6 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -640,7 +628,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "python_version <= \"3.11\" and platform_system == \"Windows\"", dev = "python_version <= \"3.11\""} +markers = {main = "platform_system == \"Windows\""} [[package]] name = "cytoolz" @@ -649,7 +637,7 @@ description = "Cython implementation of Toolz: High performance functional utili optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" and implementation_name == \"cpython\"" +markers = "implementation_name == \"cpython\"" files = [ {file = "cytoolz-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cec9af61f71fc3853eb5dca3d42eb07d1f48a4599fa502cbe92adde85f74b042"}, {file = "cytoolz-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:140bbd649dbda01e91add7642149a5987a7c3ccc251f2263de894b89f50b6608"}, @@ -766,7 +754,6 @@ description = "Python package to sign on-chain self-custodial requests for order optional = false python-versions = "<4.0,>=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "derive_action_signing-0.0.11-py3-none-any.whl", hash = "sha256:3b8c3ee7b3928ef874a9ac57cae6f7e376eeda56312c8ae0b043d47f35213c84"}, {file = "derive_action_signing-0.0.11.tar.gz", hash = "sha256:96e49a4a27e0357b0803843a9c9a42e19c9c582c3db5ac0a23621d7df93784c9"}, @@ -787,7 +774,6 @@ description = "Pythonic argument parser, that will make you smile" optional = false python-versions = "*" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] @@ -799,7 +785,6 @@ description = "eth_abi: Python utilities for working with Ethereum ABI definitio optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_abi-5.2.0-py3-none-any.whl", hash = "sha256:17abe47560ad753f18054f5b3089fcb588f3e3a092136a416b6c1502cb7e8877"}, {file = "eth_abi-5.2.0.tar.gz", hash = "sha256:178703fa98c07d8eecd5ae569e7e8d159e493ebb6eeb534a8fe973fbc4e40ef0"}, @@ -823,7 +808,6 @@ description = "eth-account: Sign Ethereum transactions and messages with local p optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_account-0.13.5-py3-none-any.whl", hash = "sha256:e43fd30c9a7fabb882b50e8c4c41d4486d2f3478ad97c66bb18cfcc872fdbec8"}, {file = "eth_account-0.13.5.tar.gz", hash = "sha256:010c9ce5f3d2688106cf9bfeb711bb8eaf0154ea6f85325f54fecea85c2b3759"}, @@ -853,7 +837,6 @@ description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (er optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_hash-0.7.1-py3-none-any.whl", hash = "sha256:0fb1add2adf99ef28883fd6228eb447ef519ea72933535ad1a0b28c6f65f868a"}, {file = "eth_hash-0.7.1.tar.gz", hash = "sha256:d2411a403a0b0a62e8247b4117932d900ffb4c8c64b15f92620547ca5ce46be5"}, @@ -866,7 +849,7 @@ pycryptodome = {version = ">=3.6.6,<4", optional = true, markers = "extra == \"p dev = ["build (>=0.9.0)", "bump_my_version (>=0.19.0)", "ipython", "mypy (==1.10.0)", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx_rtd_theme (>=1.0.0)", "towncrier (>=24,<25)", "tox (>=4.0.0)", "twine", "wheel"] docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx_rtd_theme (>=1.0.0)", "towncrier (>=24,<25)"] pycryptodome = ["pycryptodome (>=3.6.6,<4)"] -pysha3 = ["pysha3 (>=1.0.0,<2.0.0)", "safe-pysha3 (>=1.0.0)"] +pysha3 = ["pysha3 (>=1.0.0,<2.0.0) ; python_version < \"3.9\"", "safe-pysha3 (>=1.0.0) ; python_version >= \"3.9\""] test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] @@ -876,7 +859,6 @@ description = "eth-keyfile: A library for handling the encrypted keyfiles used t optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_keyfile-0.8.1-py3-none-any.whl", hash = "sha256:65387378b82fe7e86d7cb9f8d98e6d639142661b2f6f490629da09fddbef6d64"}, {file = "eth_keyfile-0.8.1.tar.gz", hash = "sha256:9708bc31f386b52cca0969238ff35b1ac72bd7a7186f2a84b86110d3c973bec1"}, @@ -899,7 +881,6 @@ description = "eth-keys: Common API for Ethereum key operations" optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_keys-0.6.1-py3-none-any.whl", hash = "sha256:7deae4cd56e862e099ec58b78176232b931c4ea5ecded2f50c7b1ccbc10c24cf"}, {file = "eth_keys-0.6.1.tar.gz", hash = "sha256:a43e263cbcabfd62fa769168efc6c27b1f5603040e4de22bb84d12567e4fd962"}, @@ -922,7 +903,6 @@ description = "eth-rlp: RLP definitions for common Ethereum objects in Python" optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_rlp-2.2.0-py3-none-any.whl", hash = "sha256:5692d595a741fbaef1203db6a2fedffbd2506d31455a6ad378c8449ee5985c47"}, {file = "eth_rlp-2.2.0.tar.gz", hash = "sha256:5e4b2eb1b8213e303d6a232dfe35ab8c29e2d3051b86e8d359def80cd21db83d"}, @@ -946,7 +926,6 @@ description = "eth-typing: Common type annotations for ethereum python packages" optional = false python-versions = ">=3.8, <4" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth-typing-4.0.0.tar.gz", hash = "sha256:9af0b6beafbc5c2e18daf19da5f5a68315023172c4e79d149e12ad10a3d3f731"}, {file = "eth_typing-4.0.0-py3-none-any.whl", hash = "sha256:7e556bea322b6e8c0a231547b736c258e10ce9eed5ddc254f51031b12af66a16"}, @@ -964,7 +943,6 @@ description = "eth-utils: Common utility functions for python code that interact optional = false python-versions = "<4,>=3.7" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "eth_utils-2.3.2-py3-none-any.whl", hash = "sha256:4470be372674a25b8440b69cb35bda634a079876930853814ea307248c3d198b"}, {file = "eth_utils-2.3.2.tar.gz", hash = "sha256:1986d704b29202386c9bc4b27b948a134320c11c8104c45ca367e4663ae7d10e"}, @@ -1005,7 +983,6 @@ description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, @@ -1023,7 +1000,6 @@ description = "A list-like structure which implements collections.abc.MutableSeq optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -1126,7 +1102,6 @@ description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -1145,7 +1120,6 @@ description = "hexbytes: Python `bytes` subclass that decodes hex, with a readab optional = false python-versions = "<4,>=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "hexbytes-1.3.0-py3-none-any.whl", hash = "sha256:83720b529c6e15ed21627962938dc2dec9bb1010f17bbbd66bf1e6a8287d522c"}, {file = "hexbytes-1.3.0.tar.gz", hash = "sha256:4a61840c24b0909a6534350e2d28ee50159ca1c9e89ce275fd31c110312cf684"}, @@ -1163,7 +1137,6 @@ description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1189,12 +1162,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -1204,7 +1177,6 @@ description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -1217,7 +1189,6 @@ description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -1233,7 +1204,6 @@ description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -1252,7 +1222,6 @@ description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -1275,7 +1244,6 @@ description = "The JSON Schema meta-schemas and vocabularies, exposed as a Regis optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -1291,7 +1259,6 @@ description = "An Dict like LRU container." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "lru-dict-1.3.0.tar.gz", hash = "sha256:54fd1966d6bd1fcde781596cb86068214edeebff1db13a2cea11079e3fd07b6b"}, {file = "lru_dict-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4073333894db9840f066226d50e6f914a2240711c87d60885d8c940b69a6673f"}, @@ -1386,7 +1353,6 @@ description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -1406,7 +1372,6 @@ description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -1432,7 +1397,6 @@ description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1504,7 +1468,6 @@ description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -1517,7 +1480,6 @@ description = "Markdown URL utilities" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1530,7 +1492,6 @@ description = "A deep merge function for 🐍." optional = false python-versions = ">=3.6" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -1543,7 +1504,6 @@ description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, @@ -1567,7 +1527,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-autorefs" @@ -1576,7 +1536,6 @@ description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mkdocs-autorefs-0.4.1.tar.gz", hash = "sha256:70748a7bd025f9ecd6d6feeba8ba63f8e891a1af55f48e366d6d6e78493aba84"}, {file = "mkdocs_autorefs-0.4.1-py3-none-any.whl", hash = "sha256:a2248a9501b29dc0cc8ba4c09f4f47ff121945f6ce33d760f145d6f89d313f5b"}, @@ -1593,7 +1552,6 @@ description = "MkDocs extension that lists all dependencies according to a mkdoc optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -1612,7 +1570,6 @@ description = "Mkdocs Markdown includer plugin." optional = false python-versions = ">=3.6" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mkdocs_include_markdown_plugin-3.9.1-py3-none-any.whl", hash = "sha256:f33687e29ac66d045ba181ea50f054646b0090b42b0a4318f08e7f1d1235e6f6"}, {file = "mkdocs_include_markdown_plugin-3.9.1.tar.gz", hash = "sha256:5e5698e78d7fea111be9873a456089daa333497988405acaac8eba2924a19152"}, @@ -1629,7 +1586,6 @@ description = "Documentation that simply works" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mkdocs_material-8.5.11-py3-none-any.whl", hash = "sha256:c907b4b052240a5778074a30a78f31a1f8ff82d7012356dc26898b97559f082e"}, {file = "mkdocs_material-8.5.11.tar.gz", hash = "sha256:b0ea0513fd8cab323e8a825d6692ea07fa83e917bb5db042e523afecc7064ab7"}, @@ -1651,7 +1607,6 @@ description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, @@ -1664,7 +1619,6 @@ description = "multidict implementation" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -1770,7 +1724,6 @@ description = "Type system extensions for programs checked with the mypy type ch optional = false python-versions = ">=3.5" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1783,7 +1736,6 @@ description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -1830,7 +1782,6 @@ description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1843,7 +1794,6 @@ description = "Powerful data structures for data analysis, time series, and stat optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -1930,7 +1880,6 @@ description = "(Soon to be) the fastest pure-Python PEG parser I could muster" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f"}, {file = "parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c"}, @@ -1946,7 +1895,6 @@ description = "Utility library for gitignore style pattern matching of file path optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1959,7 +1907,6 @@ description = "A small Python package for determining appropriate platform-speci optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1977,7 +1924,6 @@ description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1994,7 +1940,6 @@ description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d"}, {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c"}, @@ -2103,7 +2048,6 @@ description = "" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103"}, {file = "protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9"}, @@ -2123,7 +2067,6 @@ description = "Python style guide checker" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, @@ -2136,7 +2079,6 @@ description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, @@ -2179,7 +2121,6 @@ description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f"}, {file = "pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3"}, @@ -2193,7 +2134,7 @@ typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" @@ -2202,7 +2143,6 @@ description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26"}, {file = "pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927"}, @@ -2315,7 +2255,6 @@ description = "passive checker of Python programs" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, @@ -2328,7 +2267,6 @@ description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2344,7 +2282,6 @@ description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9"}, {file = "pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b"}, @@ -2364,7 +2301,6 @@ description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, @@ -2388,7 +2324,6 @@ description = "pytest plugin to re-run tests to eliminate flaky failures" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pytest-rerunfailures-13.0.tar.gz", hash = "sha256:e132dbe420bc476f544b96e7036edd0a69707574209b6677263c950d19b09199"}, {file = "pytest_rerunfailures-13.0-py3-none-any.whl", hash = "sha256:34919cb3fcb1f8e5d4b940aa75ccdea9661bade925091873b7c6fa5548333069"}, @@ -2405,7 +2340,6 @@ description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2421,7 +2355,6 @@ description = "Read key-value pairs from a .env file and set them as environment optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "python-dotenv-0.17.1.tar.gz", hash = "sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"}, {file = "python_dotenv-0.17.1-py2.py3-none-any.whl", hash = "sha256:00aa34e92d992e9f8383730816359647f358f4a3be1ba45e5a5cefd27ee91544"}, @@ -2437,7 +2370,6 @@ description = "World timezone definitions, modern and historical" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, @@ -2450,7 +2382,6 @@ description = "Unicode normalization forms (NFC, NFKC, NFD, NFKD). A library ind optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "pyunormalize-16.0.0-py3-none-any.whl", hash = "sha256:c647d95e5d1e2ea9a2f448d1d95d8518348df24eab5c3fd32d2b5c3300a49152"}, {file = "pyunormalize-16.0.0.tar.gz", hash = "sha256:2e1dfbb4a118154ae26f70710426a52a364b926c9191f764601f5a8cb12761f7"}, @@ -2463,7 +2394,7 @@ description = "Python for Window Extensions" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\" and platform_system == \"Windows\"" +markers = "platform_system == \"Windows\"" files = [ {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, @@ -2492,7 +2423,6 @@ description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2556,7 +2486,6 @@ description = "A custom YAML tag for referencing environment variables in YAML f optional = false python-versions = ">=3.6" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, @@ -2572,7 +2501,6 @@ description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -2590,7 +2518,6 @@ description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -2695,7 +2622,6 @@ description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2718,7 +2644,6 @@ description = "Render rich text, tables, progress bars, syntax highlighting, mar optional = false python-versions = ">=3.8.0" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -2739,7 +2664,6 @@ description = "Format click help output nicely with rich" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "rich_click-1.8.6-py3-none-any.whl", hash = "sha256:55fb571bad7d3d69ac43ca45f05b44616fd019616161b1815ff053567b9a8e22"}, {file = "rich_click-1.8.6.tar.gz", hash = "sha256:8a2448fd80e3d4e16fcb3815bfbc19be9bae75c9bb6aedf637901e45f3555752"}, @@ -2761,7 +2685,6 @@ description = "A package for Recursive Length Prefix encoding and decoding" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "rlp-3.0.0-py2.py3-none-any.whl", hash = "sha256:d2a963225b3f26795c5b52310e0871df9824af56823d739511583ef459895a7d"}, {file = "rlp-3.0.0.tar.gz", hash = "sha256:63b0465d2948cd9f01de449d7adfb92d207c1aef3982f20310f8009be4a507e8"}, @@ -2784,7 +2707,6 @@ description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "rpds_py-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724"}, {file = "rpds_py-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b"}, @@ -2909,7 +2831,6 @@ description = "Simple data validation library" optional = false python-versions = "*" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "schema-0.7.7-py2.py3-none-any.whl", hash = "sha256:5d976a5b50f36e74e2157b47097b60002bd4d42e65425fcc9c9befadb4255dde"}, {file = "schema-0.7.7.tar.gz", hash = "sha256:7da553abd2958a19dc2547c388cde53398b39196175a9be59ea1caf5ab0a1807"}, @@ -2922,7 +2843,6 @@ description = "Python helper for Semantic Versioning (http://semver.org/)" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, @@ -2935,20 +2855,19 @@ description = "Easily download, build, install, upgrade, and uninstall Python pa optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f"}, {file = "setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -2957,7 +2876,6 @@ description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2970,7 +2888,6 @@ description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -2986,7 +2903,6 @@ description = "Bump software releases" optional = false python-versions = ">=3.7,<4.0" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "tbump-6.11.0-py3-none-any.whl", hash = "sha256:6b181fe6f3ae84ce0b9af8cc2009a8bca41ded34e73f623a7413b9684f1b4526"}, {file = "tbump-6.11.0.tar.gz", hash = "sha256:385e710eedf0a8a6ff959cf1e9f3cfd17c873617132fc0ec5f629af0c355c870"}, @@ -3048,7 +2964,6 @@ description = "Style preserving TOML library" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, @@ -3061,7 +2976,7 @@ description = "List processing tools and functional utilities" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" and (implementation_name == \"cpython\" or implementation_name == \"pypy\")" +markers = "implementation_name == \"cpython\" or implementation_name == \"pypy\"" files = [ {file = "toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236"}, {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, @@ -3078,7 +2993,7 @@ files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {main = "python_version <= \"3.11\"", dev = "python_version < \"3.11\""} +markers = {dev = "python_version < \"3.11\""} [[package]] name = "typing-inspection" @@ -3087,7 +3002,6 @@ description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, @@ -3103,7 +3017,6 @@ description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, @@ -3116,7 +3029,6 @@ description = "ASCII transliterations of Unicode text" optional = false python-versions = ">=3.5" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39"}, {file = "Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4"}, @@ -3129,14 +3041,13 @@ description = "HTTP library with thread-safe connection pooling, file post, and optional = false python-versions = ">=3.9" groups = ["main", "dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -3148,7 +3059,6 @@ description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version <= \"3.11\"" files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -3192,7 +3102,6 @@ description = "web3.py" optional = false python-versions = ">=3.7.2" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "web3-6.11.0-py3-none-any.whl", hash = "sha256:44e79da6a4765eacf137f2f388e37aa0c1e24a93bdfb462cffe9441d1be3d509"}, {file = "web3-6.11.0.tar.gz", hash = "sha256:050dea52ae73d787272e7ecba7249f096595938c90cce1a384c20375c6b0f720"}, @@ -3216,7 +3125,7 @@ typing-extensions = ">=4.0.1" websockets = ">=10.0.0" [package.extras] -dev = ["black (>=22.1.0)", "build (>=0.9.0)", "bumpversion", "eth-tester[py-evm] (==v0.9.1-b.1)", "flake8 (==3.8.3)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "importlib-metadata (<5.0)", "ipfshttpclient (==0.8.0a2)", "isort (>=5.11.0)", "mypy (==1.4.1)", "py-geth (>=3.11.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1)", "pytest-mock (>=1.10)", "pytest-watch (>=4.2)", "pytest-xdist (>=1.29)", "setuptools (>=38.6.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=3.18.0)", "tqdm (>4.32)", "twine (>=1.13)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)", "when-changed (>=0.3.0)"] +dev = ["black (>=22.1.0)", "build (>=0.9.0)", "bumpversion", "eth-tester[py-evm] (==v0.9.1-b.1)", "flake8 (==3.8.3)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "importlib-metadata (<5.0) ; python_version < \"3.8\"", "ipfshttpclient (==0.8.0a2)", "isort (>=5.11.0)", "mypy (==1.4.1)", "py-geth (>=3.11.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1)", "pytest-mock (>=1.10)", "pytest-watch (>=4.2)", "pytest-xdist (>=1.29)", "setuptools (>=38.6.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=3.18.0)", "tqdm (>4.32)", "twine (>=1.13)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)", "when-changed (>=0.3.0)"] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] ipfs = ["ipfshttpclient (==0.8.0a2)"] linter = ["black (>=22.1.0)", "flake8 (==3.8.3)", "isort (>=5.11.0)", "mypy (==1.4.1)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)"] @@ -3229,7 +3138,6 @@ description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "websocket-client-0.59.0.tar.gz", hash = "sha256:d376bd60eace9d437ab6d7ee16f4ab4e821c9dae591e1b783c58ebd8aaf80c5c"}, {file = "websocket_client-0.59.0-py2.py3-none-any.whl", hash = "sha256:2e50d26ca593f70aba7b13a489435ef88b8fc3b5c5643c1ce8808ff9b40f0b32"}, @@ -3245,7 +3153,6 @@ description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"}, {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"}, @@ -3325,7 +3232,6 @@ description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\"" files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -3430,14 +3336,14 @@ files = [ ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<3.12" -content-hash = "4e2dbb50a9343b486366e57f07635cb59f3279aa12986fac8cb16fd2ef017bc7" +content-hash = "3838d68f3b49f6e50df5a46234fc57e45f547a8519710328d9c1ac73e4e2314b" diff --git a/pyproject.toml b/pyproject.toml index 758acf30..43b74b72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ drv = "derive_client.cli:cli" [tool.poetry.group.dev.dependencies] pytest = "^7.4.2" -black = "^23.10.1" +black = "^24" isort = "^5.12.0" flake8 = "^6.1.0" tbump = "^6.11.0" From 7e67bf8d1c3af401f1488febe4b9c65e97c46f3e Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 00:13:18 +0200 Subject: [PATCH 07/15] chore: make fmt lint --- derive_client/__init__.py | 1 + derive_client/cli.py | 1 + derive_client/clients/base_client.py | 20 +++++++++++++++----- derive_client/data_types/models.py | 9 +++------ examples/fetch_instruments.py | 1 + tests/test_main.py | 1 + tests/test_rfq.py | 1 - tests/test_user.py | 1 - 8 files changed, 22 insertions(+), 13 deletions(-) diff --git a/derive_client/__init__.py b/derive_client/__init__.py index 58db692e..b7c9956d 100644 --- a/derive_client/__init__.py +++ b/derive_client/__init__.py @@ -1,6 +1,7 @@ """ Init for the derive client """ + from .derive import DeriveClient DeriveClient diff --git a/derive_client/cli.py b/derive_client/cli.py index 28a8de74..7a2fc4cd 100644 --- a/derive_client/cli.py +++ b/derive_client/cli.py @@ -1,6 +1,7 @@ """ Cli module in order to allow interaction. """ + import os from textwrap import dedent diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index 4838f6e8..ae75bcec 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -1,15 +1,15 @@ """ Base Client for the derive dex. """ + import json import random from decimal import Decimal -from time import sleep from logging import Logger +from time import sleep import eth_abi import requests -from pydantic import validate_arguments from derive_action_signing.module_data import ( DepositModuleData, RecipientTransferERC20ModuleData, @@ -20,12 +20,20 @@ ) from derive_action_signing.signed_action import SignedAction from derive_action_signing.utils import MAX_INT_32, get_action_nonce, sign_rest_auth_header, sign_ws_login, utc_now_ms +from pydantic import validate_arguments from rich import print from web3 import Web3 from websocket import WebSocketConnectionClosedException, create_connection from derive_client._bridge import BridgeClient -from derive_client.constants import CONFIGS, DEFAULT_REFERER, PUBLIC_HEADERS, TARGET_SPEED, TOKEN_DECIMALS, LIGHT_ACCOUNT_ABI_PATH +from derive_client.constants import ( + CONFIGS, + DEFAULT_REFERER, + LIGHT_ACCOUNT_ABI_PATH, + PUBLIC_HEADERS, + TARGET_SPEED, + TOKEN_DECIMALS, +) from derive_client.data_types import ( Address, ChainID, @@ -90,12 +98,14 @@ def __init__( def _verify_wallet(self, wallet: Address) -> Address: w3 = get_w3_connection(ChainID.DERIVE) if not w3.eth.get_code(wallet): - raise ValueError(f"{wallet} appears to be an EOA (no bytecode). Expected a smart-contract wallet on Derive.") + msg = f"{wallet} appears to be an EOA (no bytecode). Expected a smart-contract wallet on Derive." + raise ValueError(msg) abi = json.loads(LIGHT_ACCOUNT_ABI_PATH.read_text()) contract = w3.eth.contract(address=wallet, abi=abi) owner = contract.functions.owner().call() if not owner == self.signer.address: - raise ValueError(f"Smart Contract wallet owner mismatch: on-chain owner={owner}, signer={self.signer.address}") + msg = f"Smart Contract wallet owner mismatch: on-chain owner={owner}, signer={self.signer.address}" + raise ValueError(msg) return wallet def _determine_subaccount_id(self, subaccount_id: int | None) -> int: diff --git a/derive_client/data_types/models.py b/derive_client/data_types/models.py index 32ad08b3..2133d281 100644 --- a/derive_client/data_types/models.py +++ b/derive_client/data_types/models.py @@ -1,21 +1,18 @@ """Models used in the bridge module.""" - from dataclasses import dataclass from derive_action_signing.module_data import ModuleData from derive_action_signing.utils import decimal_to_big_int from eth_abi.abi import encode -from pydantic import BaseModel, ConfigDict +from eth_utils import is_address, to_checksum_address +from pydantic import BaseModel, ConfigDict, GetCoreSchemaHandler, GetJsonSchemaHandler +from pydantic_core import core_schema from web3 import Web3 from web3.datastructures import AttributeDict from .enums import ChainID, Currency, TxStatus -from pydantic_core import core_schema -from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler -from eth_utils import to_checksum_address, is_address - class Address(str): @classmethod diff --git a/examples/fetch_instruments.py b/examples/fetch_instruments.py index 896a9a06..c1be0e5b 100644 --- a/examples/fetch_instruments.py +++ b/examples/fetch_instruments.py @@ -1,6 +1,7 @@ """ Sample of fetching instruments from the derive client, and printing the result. """ + from rich import print from derive_client.data_types import Environment, InstrumentType, UnderlyingCurrency diff --git a/tests/test_main.py b/tests/test_main.py index 6ec3c53a..175ebddf 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,6 +1,7 @@ """ Tests for the main function. """ + import time from itertools import product diff --git a/tests/test_rfq.py b/tests/test_rfq.py index 458ca76f..50c9df99 100644 --- a/tests/test_rfq.py +++ b/tests/test_rfq.py @@ -2,7 +2,6 @@ Implement tests for the RFQ class. """ - from dataclasses import asdict, dataclass import pytest diff --git a/tests/test_user.py b/tests/test_user.py index 165a1ecf..8c0abedf 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -2,7 +2,6 @@ User tests """ - from time import sleep import pytest From 147de82b31c69bc726658a876e1d94d9f0fd875b Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 02:11:16 +0200 Subject: [PATCH 08/15] feat: SessionKey --- derive_client/data_types/__init__.py | 1 + derive_client/data_types/enums.py | 6 ++++++ derive_client/data_types/models.py | 10 +++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/derive_client/data_types/__init__.py b/derive_client/data_types/__init__.py index aa84dc6d..14be7ba5 100644 --- a/derive_client/data_types/__init__.py +++ b/derive_client/data_types/__init__.py @@ -25,6 +25,7 @@ MintableTokenData, NonMintableTokenData, TxResult, + SessionKey, ) __all__ = [ diff --git a/derive_client/data_types/enums.py b/derive_client/data_types/enums.py index 35a14a75..0c39e3ed 100644 --- a/derive_client/data_types/enums.py +++ b/derive_client/data_types/enums.py @@ -38,6 +38,12 @@ class RPCEndPoints(Enum): DERIVE = LYRA = "https://rpc.lyra.finance" +class SessionKeyScope(Enum): + ADMIN = "admin" + ACCOUNT = "account" + READ_ONLY = "read_only" + + class InstrumentType(Enum): """Instrument types.""" diff --git a/derive_client/data_types/models.py b/derive_client/data_types/models.py index 2133d281..dfb5519f 100644 --- a/derive_client/data_types/models.py +++ b/derive_client/data_types/models.py @@ -11,7 +11,7 @@ from web3 import Web3 from web3.datastructures import AttributeDict -from .enums import ChainID, Currency, TxStatus +from .enums import ChainID, Currency, TxStatus, SessionKeyScope class Address(str): @@ -84,6 +84,14 @@ class DeriveAddresses(BaseModel): chains: dict[ChainID, dict[Currency, MintableTokenData | NonMintableTokenData]] +class SessionKey(BaseModel): + public_session_key: Address + expiry_sec: int + ip_whitelist: list + label: str + scope: SessionKeyScope + + @dataclass class TxResult: tx_hash: str From 1e68d92f68481ed61e0bae7f6fbc5101b607538f Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 02:12:24 +0200 Subject: [PATCH 09/15] feat: add rpc_endpoint and block_explorer to EnvConfig --- derive_client/constants.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/derive_client/constants.py b/derive_client/constants.py index ff485a02..40a5a367 100644 --- a/derive_client/constants.py +++ b/derive_client/constants.py @@ -34,6 +34,8 @@ def __getitem__(self, key): class EnvConfig(BaseModel, frozen=True): base_url: str ws_address: str + rpc_endpoint: str + block_explorer: str ACTION_TYPEHASH: str DOMAIN_SEPARATOR: str contracts: ContractAddresses @@ -52,6 +54,8 @@ class EnvConfig(BaseModel, frozen=True): Environment.TEST: EnvConfig( base_url="https://api-demo.lyra.finance", ws_address="wss://api-demo.lyra.finance/ws", + rpc_endpoint="https://rpc-prod-testnet-0eakp60405.t.conduit.xyz", + block_explorer="https://explorer-prod-testnet-0eakp60405.t.conduit.xyz", ACTION_TYPEHASH="0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17", DOMAIN_SEPARATOR="0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105", contracts=ContractAddresses( @@ -76,6 +80,8 @@ class EnvConfig(BaseModel, frozen=True): Environment.PROD: EnvConfig( base_url="https://api.lyra.finance", ws_address="wss://api.lyra.finance/ws", + rpc_endpoint="https://rpc.lyra.finance", + block_explorer="https://explorer.lyra.finance", ACTION_TYPEHASH="0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17", DOMAIN_SEPARATOR="0xd96e5f90797da7ec8dc4e276260c7f3f87fedf68775fbe1ef116e996fc60441b", contracts=ContractAddresses( From b8dc4a38f2761a8d4f541896dda37982f29bd52e Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 02:13:08 +0200 Subject: [PATCH 10/15] feat: verify session key registration --- derive_client/clients/base_client.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index ae75bcec..a33b57c8 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -51,6 +51,7 @@ TimeInForce, TxResult, UnderlyingCurrency, + SessionKey, ) from derive_client.utils import get_logger, get_prod_derive_addresses, get_w3_connection @@ -91,22 +92,22 @@ def __init__( self.logger = logger or get_logger() self.web3_client = Web3() self.signer = self.web3_client.eth.account.from_key(private_key) - self.wallet = self._verify_wallet(wallet) + self.wallet = wallet + self._verify_wallet(wallet) self.subaccount_id = self._determine_subaccount_id(subaccount_id) self.referral_code = referral_code - def _verify_wallet(self, wallet: Address) -> Address: - w3 = get_w3_connection(ChainID.DERIVE) + def _verify_wallet(self, wallet: Address): + w3 = Web3(Web3.HTTPProvider(self.config.rpc_endpoint)) + if not w3.is_connected(): + raise ConnectionError(f"Failed to connect to RPC at {rpc_url}") if not w3.eth.get_code(wallet): msg = f"{wallet} appears to be an EOA (no bytecode). Expected a smart-contract wallet on Derive." raise ValueError(msg) - abi = json.loads(LIGHT_ACCOUNT_ABI_PATH.read_text()) - contract = w3.eth.contract(address=wallet, abi=abi) - owner = contract.functions.owner().call() - if not owner == self.signer.address: - msg = f"Smart Contract wallet owner mismatch: on-chain owner={owner}, signer={self.signer.address}" + session_keys = self._get_session_keys(wallet) + if not any(self.signer.address == s.public_session_key for s in session_keys): + msg = f"{self.signer.address} is not among registered session keys for wallet {wallet}." raise ValueError(msg) - return wallet def _determine_subaccount_id(self, subaccount_id: int | None) -> int: subaccounts = self.fetch_subaccounts() @@ -205,6 +206,15 @@ def fetch_instruments( } return self._send_request(url, json=payload, headers=PUBLIC_HEADERS) + def _get_session_keys(self, wallet: Address) -> list[SessionKey]: + url = f"{self.config.base_url}/private/session_keys" + payload = {"wallet": wallet} + session_keys = self._send_request(url, json=payload) + if not (public_session_keys := session_keys.get("public_session_keys")): + msg = f"No session keys registered for this wallet: {wallet}" + raise ValueError(msg) + return list(map(lambda kwargs: SessionKey(**kwargs), public_session_keys)) + def fetch_subaccounts(self): """ Returns the subaccounts for a given wallet From 87d9b62f1e6f8a41ff3550932b39c7adf5485987 Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 10:33:25 +0200 Subject: [PATCH 11/15] feat: ManagerAddress --- derive_client/data_types/__init__.py | 3 +++ derive_client/data_types/enums.py | 11 +++++++++++ derive_client/data_types/models.py | 8 +++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/derive_client/data_types/__init__.py b/derive_client/data_types/__init__.py index 14be7ba5..e327a740 100644 --- a/derive_client/data_types/__init__.py +++ b/derive_client/data_types/__init__.py @@ -16,6 +16,8 @@ TimeInForce, TxStatus, UnderlyingCurrency, + MarginType, + MainnetCurrency, ) from .models import ( Address, @@ -26,6 +28,7 @@ NonMintableTokenData, TxResult, SessionKey, + ManagerAddress, ) __all__ = [ diff --git a/derive_client/data_types/enums.py b/derive_client/data_types/enums.py index 0c39e3ed..a0aebd9c 100644 --- a/derive_client/data_types/enums.py +++ b/derive_client/data_types/enums.py @@ -44,6 +44,17 @@ class SessionKeyScope(Enum): READ_ONLY = "read_only" +class MainnetCurrency(Enum): + BTC = "BTC" + ETH = "ETH" + + +class MarginType(Enum): + SM = "SM" + PM = "PM" + PM2 = "PM2" + + class InstrumentType(Enum): """Instrument types.""" diff --git a/derive_client/data_types/models.py b/derive_client/data_types/models.py index dfb5519f..eaa66e7c 100644 --- a/derive_client/data_types/models.py +++ b/derive_client/data_types/models.py @@ -11,7 +11,7 @@ from web3 import Web3 from web3.datastructures import AttributeDict -from .enums import ChainID, Currency, TxStatus, SessionKeyScope +from .enums import ChainID, Currency, TxStatus, SessionKeyScope, MarginType, MainnetCurrency class Address(str): @@ -92,6 +92,12 @@ class SessionKey(BaseModel): scope: SessionKeyScope +class ManagerAddress(BaseModel): + address: Address + margin_type: MarginType + currency: MainnetCurrency | None + + @dataclass class TxResult: tx_hash: str From 82388c4ad75bea272f1eb39ef204a576b682e44a Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 10:34:19 +0200 Subject: [PATCH 12/15] fix: get_manager_for_subaccount --- derive_client/clients/base_client.py | 38 +++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index a33b57c8..37ba78e4 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -50,8 +50,11 @@ SubaccountType, TimeInForce, TxResult, + MarginType, + MainnetCurrency, UnderlyingCurrency, SessionKey, + ManagerAddress, ) from derive_client.utils import get_logger, get_prod_derive_addresses, get_w3_connection @@ -818,24 +821,25 @@ def get_manager_for_subaccount(self, subaccount_id: int, asset_name): deposit_currency = UnderlyingCurrency[asset_name] currency = self.fetch_currency(asset_name) underlying_address = currency['protocol_asset_addresses']['spot'] - manager_addresses = currency['managers'] - - if len(manager_addresses) == 1: - manager_address = manager_addresses[0].get('address') - else: - to_account = self.fetch_subaccount(subaccount_id) - account_type = ( - SubaccountType.STANDARD if to_account.get("margin_type") == "SM" else SubaccountType.PORTFOLIO - ) - account_currency = UnderlyingCurrency[to_account.get("currency")] - index = ( - 0 if account_type is SubaccountType.STANDARD else 1 if account_currency is UnderlyingCurrency.ETH else 2 - ) - manager_address = manager_addresses[index].get('address') - - if not manager_address or not underlying_address: + managers = list(map(lambda kwargs: ManagerAddress(**kwargs), currency['managers'])) + manager_by_type = {} + for manager in managers: + manager_by_type.setdefault((manager.margin_type, manager.currency), []).append(manager) + + to_account = self.fetch_subaccount(subaccount_id) + account_currency = MainnetCurrency[to_account.get("currency")] + margin_type = MarginType[to_account.get("margin_type")] + + def get_unique_manager(margin_type, currency): + matches = manager_by_type.get((margin_type, currency), []) + if len(matches) != 1: + raise ValueError(f"Expected exactly one ManagerAddress for {(margin_type, currency)}, found {matches}") + return matches[0] + + manager = get_unique_manager(margin_type, account_currency) + if not manager.address or not underlying_address: raise Exception(f"Unable to find manager address or underlying address for {asset_name}") - return manager_address, underlying_address, TOKEN_DECIMALS[deposit_currency] + return manager.address, underlying_address, TOKEN_DECIMALS[deposit_currency] def transfer_from_subaccount_to_funding(self, amount: int, asset_name: str, subaccount_id: int): """ From d10296e9fa22c64863ffe484c8f398a507f9574c Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 11:22:37 +0200 Subject: [PATCH 13/15] chore: make fmt lint --- derive_client/clients/base_client.py | 20 +++++++------------- derive_client/data_types/__init__.py | 12 ++++++++---- derive_client/data_types/models.py | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/derive_client/clients/base_client.py b/derive_client/clients/base_client.py index 37ba78e4..edf8efea 100644 --- a/derive_client/clients/base_client.py +++ b/derive_client/clients/base_client.py @@ -26,14 +26,7 @@ from websocket import WebSocketConnectionClosedException, create_connection from derive_client._bridge import BridgeClient -from derive_client.constants import ( - CONFIGS, - DEFAULT_REFERER, - LIGHT_ACCOUNT_ABI_PATH, - PUBLIC_HEADERS, - TARGET_SPEED, - TOKEN_DECIMALS, -) +from derive_client.constants import CONFIGS, DEFAULT_REFERER, PUBLIC_HEADERS, TARGET_SPEED, TOKEN_DECIMALS from derive_client.data_types import ( Address, ChainID, @@ -43,18 +36,18 @@ Currency, Environment, InstrumentType, + MainnetCurrency, + ManagerAddress, + MarginType, OrderSide, OrderStatus, OrderType, RfqStatus, + SessionKey, SubaccountType, TimeInForce, TxResult, - MarginType, - MainnetCurrency, UnderlyingCurrency, - SessionKey, - ManagerAddress, ) from derive_client.utils import get_logger, get_prod_derive_addresses, get_w3_connection @@ -103,7 +96,7 @@ def __init__( def _verify_wallet(self, wallet: Address): w3 = Web3(Web3.HTTPProvider(self.config.rpc_endpoint)) if not w3.is_connected(): - raise ConnectionError(f"Failed to connect to RPC at {rpc_url}") + raise ConnectionError(f"Failed to connect to RPC at {self.config.rpc_endpoint}") if not w3.eth.get_code(wallet): msg = f"{wallet} appears to be an EOA (no bytecode). Expected a smart-contract wallet on Derive." raise ValueError(msg) @@ -290,6 +283,7 @@ def create_order( "referral_code": DEFAULT_REFERER if not self.referral_code else self.referral_code, **signed_action.to_json(), } + # breakpoint() response = self.submit_order(order) return response diff --git a/derive_client/data_types/__init__.py b/derive_client/data_types/__init__.py index e327a740..18333a03 100644 --- a/derive_client/data_types/__init__.py +++ b/derive_client/data_types/__init__.py @@ -7,6 +7,8 @@ Currency, Environment, InstrumentType, + MainnetCurrency, + MarginType, OrderSide, OrderStatus, OrderType, @@ -16,19 +18,17 @@ TimeInForce, TxStatus, UnderlyingCurrency, - MarginType, - MainnetCurrency, ) from .models import ( Address, CreateSubAccountData, CreateSubAccountDetails, DeriveAddresses, + ManagerAddress, MintableTokenData, NonMintableTokenData, - TxResult, SessionKey, - ManagerAddress, + TxResult, ) __all__ = [ @@ -49,9 +49,13 @@ "ActionType", "RfqStatus", "Address", + "SessionKey", "MintableTokenData", "NonMintableTokenData", "DeriveAddresses", "CreateSubAccountDetails", "CreateSubAccountData", + "MainnetCurrency", + "MarginType", + "ManagerAddress", ] diff --git a/derive_client/data_types/models.py b/derive_client/data_types/models.py index eaa66e7c..c3ed9d28 100644 --- a/derive_client/data_types/models.py +++ b/derive_client/data_types/models.py @@ -11,7 +11,7 @@ from web3 import Web3 from web3.datastructures import AttributeDict -from .enums import ChainID, Currency, TxStatus, SessionKeyScope, MarginType, MainnetCurrency +from .enums import ChainID, Currency, MainnetCurrency, MarginType, SessionKeyScope, TxStatus class Address(str): From e2ade1e12d6530c29819e239c64482b6a0e4c625 Mon Sep 17 00:00:00 2001 From: zarathustra Date: Thu, 8 May 2025 11:22:56 +0200 Subject: [PATCH 14/15] chore: disable test that continuously timeout --- tests/test_main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_main.py b/tests/test_main.py index 175ebddf..a8eee6b0 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -96,6 +96,7 @@ def test_create_sm_subaccount(derive_client): ("ETH-PERP", OrderSide.SELL, 10000, InstrumentType.PERP), ], ) +@pytest.mark.skip("'Order confirmation timeout' on v2-action-signing-python/examples/order.py") def test_create_order(derive_client, instrument_name, side, price, instrument_type): """ Test the DeriveClient class. @@ -181,6 +182,7 @@ def test_fetch_orders(derive_client): assert orders +@pytest.mark.skip("'Order confirmation timeout' on v2-action-signing-python/examples/order.py") def test_cancel_order(derive_client): """ Test the DeriveClient class. @@ -197,6 +199,7 @@ def test_cancel_order(derive_client): assert result['order_id'] == order_id +@pytest.mark.skip("'Order confirmation timeout' on v2-action-signing-python/examples/order.py") def test_cancel_all_orders(derive_client): """Test all open orders are cancelled.""" derive_client.create_order( From 46a0e19eb0b1a754496b323a9420f6e4b308efef Mon Sep 17 00:00:00 2001 From: zarathustra Date: Fri, 9 May 2025 00:10:37 +0200 Subject: [PATCH 15/15] chore: re-enable create order tests previously timing out --- tests/test_main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index a8eee6b0..175ebddf 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -96,7 +96,6 @@ def test_create_sm_subaccount(derive_client): ("ETH-PERP", OrderSide.SELL, 10000, InstrumentType.PERP), ], ) -@pytest.mark.skip("'Order confirmation timeout' on v2-action-signing-python/examples/order.py") def test_create_order(derive_client, instrument_name, side, price, instrument_type): """ Test the DeriveClient class. @@ -182,7 +181,6 @@ def test_fetch_orders(derive_client): assert orders -@pytest.mark.skip("'Order confirmation timeout' on v2-action-signing-python/examples/order.py") def test_cancel_order(derive_client): """ Test the DeriveClient class. @@ -199,7 +197,6 @@ def test_cancel_order(derive_client): assert result['order_id'] == order_id -@pytest.mark.skip("'Order confirmation timeout' on v2-action-signing-python/examples/order.py") def test_cancel_all_orders(derive_client): """Test all open orders are cancelled.""" derive_client.create_order(