diff --git a/README.md b/README.md index aafb055..5d00147 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # TONsdk -[![PyPI](https://img.shields.io/pypi/v/tonsdk?color=blue)](https://pypi.org/project/tonsdk/) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tonsdk)](https://pypi.org/project/tonsdk/) -[![Downloads](https://static.pepy.tech/badge/tonsdk)](https://pepy.tech/project/tonsdk) +[![PyPI](https://img.shields.io/pypi/v/tonsdk?color=blue)](https://pypi.org/project/tonsdk_ng/) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tonsdk_ng)](https://pypi.org/project/tonsdk_ng/) +[![Downloads](https://static.pepy.tech/badge/tonsdk_ng)](https://pepy.tech/project/tonsdk_ng) ## Description This low-level Python library allows you to work with the [TON blockchain](https://ton.org/). @@ -13,79 +13,80 @@ This low-level Python library allows you to work with the [TON blockchain](https ## How to install ```bash -pip install tonsdk +pip install tonsdk_ng ``` ## How to use -You can find examples in [examples](https://github.com/tonfactory/tonsdk/tree/master/examples) folder +You can find examples in [examples](https://github.com/gtors/tonsdk_ng/tree/master/examples) folder ## General usage examples ### Create mnemonic, init wallet class, create external message to deploy the wallet ```python -from tonsdk.contract.wallet import WalletVersionEnum, Wallets -from tonsdk.utils import bytes_to_b64str -from tonsdk.crypto import mnemonic_new +from tonsdk_ng.contract.wallet import Wallets, WalletVersionEnum +from tonsdk_ng.crypto import mnemonic_new +from tonsdk_ng.utils import bytes_to_b64str wallet_workchain = 0 -wallet_version = WalletVersionEnum.v3r2 +wallet_version = WalletVersionEnum.v4r2 wallet_mnemonics = mnemonic_new() _mnemonics, _pub_k, _priv_k, wallet = Wallets.from_mnemonics( - wallet_mnemonics, wallet_version, wallet_workchain) + wallet_mnemonics, + wallet_version, + wallet_workchain, +) query = wallet.create_init_external_message() base64_boc = bytes_to_b64str(query["message"].to_boc(False)) -print(""" -Mnemonic: {} +print( + f""" + Mnemonic: {wallet_mnemonics} -Raw address: {} + Raw address: {wallet.address.to_string()} -Bounceable, url safe, user friendly address: {} + Bounceable, url safe, user friendly address: {wallet.address.to_string(True, True, True)} -Base64boc to deploy the wallet: {} -""".format(wallet_mnemonics, - wallet.address.to_string(), - wallet.address.to_string(True, True, True), - base64_boc)) + Base64boc to deploy the wallet: {base64_boc} + """ +) ``` ### Transfer NFT & Jettons by creating a transfer message from an owner wallet ```python -from tonsdk.contract.token.nft import NFTItem -from tonsdk.contract.token.ft import JettonWallet -from tonsdk.utils import Address, to_nano +from tonsdk_ng.contract.token.ft import JettonWallet +from tonsdk_ng.contract.token.nft import NFTItem +from tonsdk_ng.utils import Address, to_nano -body = NFTItem().create_transfer_body( - Address("New Owner Address") -) +body = NFTItem().create_transfer_body(Address("New Owner Address")) query = wallet.create_transfer_message( "NFT Item Address", to_nano(0.05, "ton"), 0, # owner wallet seqno - payload=body + payload=body, ) nft_boc = bytes_to_b64str(query["message"].to_boc(False)) body = JettonWallet().create_transfer_body( - Address("Destination address"), - to_nano(40000, "ton") # jettons amount + Address("Destination address"), to_nano(40000, "ton") # jettons amount ) query = wallet.create_transfer_message( "Jetton Wallet Address", to_nano(0.05, "ton"), 0, # owner wallet seqno - payload=body + payload=body, ) jettons_boc = bytes_to_b64str(query["message"].to_boc(False)) -print(""" -Base64boc to transfer the NFT item: {} +print( + f""" + Base64boc to transfer the NFT item: {nft_boc} -Base64boc to transfer the jettons: {} -""".format(nft_boc, jettons_boc)) + Base64boc to transfer the jettons: {jettons_boc} + """ +) ``` ### Clients usage example (dirty) @@ -93,14 +94,20 @@ Base64boc to transfer the jettons: {} *Note - to use these clients you should install tvm_valuetypes and aiohttp packages* ```python -from abc import ABC, abstractmethod import asyncio +from abc import ABC, abstractmethod + import aiohttp from tvm_valuetypes import serialize_tvm_stack -from tonsdk.provider import ToncenterClient, SyncTonlibClient, prepare_address, address_state -from tonsdk.utils import TonCurrencyEnum, from_nano -from tonsdk.boc import Cell +from tonsdk_ng.boc import Cell +from tonsdk_ng.provider import ( + SyncTonlibClient, + ToncenterClient, + address_state, + prepare_address, +) +from tonsdk_ng.utils import TonCurrencyEnum, from_nano class AbstractTonClient(ABC): @@ -108,12 +115,16 @@ class AbstractTonClient(ABC): def _run(self, to_run, *, single_query=True): raise NotImplemented - def get_address_information(self, address: str, - currency_to_show: TonCurrencyEnum = TonCurrencyEnum.ton): + def get_address_information( + self, + address: str, + currency_to_show: TonCurrencyEnum = TonCurrencyEnum.ton, + ): return self.get_addresses_information([address], currency_to_show)[0] - def get_addresses_information(self, addresses, - currency_to_show: TonCurrencyEnum = TonCurrencyEnum.ton): + def get_addresses_information( + self, addresses, currency_to_show: TonCurrencyEnum = TonCurrencyEnum.ton + ): if not addresses: return [] @@ -131,16 +142,19 @@ class AbstractTonClient(ABC): result["balance"] = 0 else: result["balance"] = from_nano( - int(result["balance"]), currency_to_show) + int(result["balance"]), currency_to_show + ) return results - + def seqno(self, addr: str): addr = prepare_address(addr) result = self._run(self.provider.raw_run_method(addr, "seqno", [])) - if 'stack' in result and ('@type' in result and result['@type'] == 'smc.runResult'): - result['stack'] = serialize_tvm_stack(result['stack']) + if "stack" in result and ( + "@type" in result and result["@type"] == "smc.runResult" + ): + result["stack"] = serialize_tvm_stack(result["stack"]) return result @@ -151,15 +165,20 @@ class AbstractTonClient(ABC): class TonCenterTonClient(AbstractTonClient): def __init__(self): self.loop = asyncio.get_event_loop() - self.provider = ToncenterClient(base_url="https://testnet.toncenter.com/api/v2/", - api_key="eb542b65e88d2da318fb7c163b9245e4edccb2eb8ba11cabda092cdb6fbc3395") + self.provider = ToncenterClient( + base_url="https://testnet.toncenter.com/api/v2/", + api_key="eb542b65e88d2da318fb7c163b9245e4edccb2eb8ba11cabda092cdb6fbc3395", + ) def _run(self, to_run, *, single_query=True): try: return self.loop.run_until_complete( - self.__execute(to_run, single_query)) + self.__execute(to_run, single_query) + ) - except Exception: # ToncenterWrongResult, asyncio.exceptions.TimeoutError, aiohttp.client_exceptions.ClientConnectorError + except ( + Exception + ): # ToncenterWrongResult, asyncio.exceptions.TimeoutError, aiohttp.client_exceptions.ClientConnectorError raise async def __execute(self, to_run, single_query): @@ -171,8 +190,9 @@ class TonCenterTonClient(AbstractTonClient): tasks = [] for task in to_run: - tasks.append(task["func"]( - session, *task["args"], **task["kwargs"])) + tasks.append( + task["func"](session, *task["args"], **task["kwargs"]) + ) return await asyncio.gather(*tasks) @@ -180,16 +200,19 @@ class TonCenterTonClient(AbstractTonClient): class TonLibJsonTonClient(AbstractTonClient): def __init__(self): self.loop = asyncio.get_event_loop() - self.provider = SyncTonlibClient(config="./.tonlibjson/testnet.json", - keystore="./.tonlibjson/keystore", - cdll_path="./.tonlibjson/linux_libtonlibjson.so") # or macos_libtonlibjson.dylib + self.provider = SyncTonlibClient( + config="./.tonlibjson/testnet.json", + keystore="./.tonlibjson/keystore", + cdll_path="./.tonlibjson/linux_libtonlibjson.so", + ) # or macos_libtonlibjson.dylib self.provider.init() def _run(self, to_read, *, single_query=True): try: if not single_query: - queries_order = {query_id: i for i, - query_id in enumerate(to_read)} + queries_order = { + query_id: i for i, query_id in enumerate(to_read) + } return self.provider.read_results(queries_order) else: @@ -204,7 +227,8 @@ client = TonCenterTonClient() # use client to get any addr information addr_info = client.get_address_information( - "EQAhE3sLxHZpsyZ_HecMuwzvXHKLjYx4kEUehhOy2JmCcHCT") + "EQAhE3sLxHZpsyZ_HecMuwzvXHKLjYx4kEUehhOy2JmCcHCT" +) # get your wallet seqno seqno = client.seqno(wallet.address.to_string()) diff --git a/examples/tokens/jetton/mint.py b/examples/tokens/jetton/mint.py index 2325c5f..896b286 100644 --- a/examples/tokens/jetton/mint.py +++ b/examples/tokens/jetton/mint.py @@ -1,7 +1,7 @@ -from tonsdk.contract.token.ft import JettonMinter, JettonWallet -from tonsdk.contract import Address -from tonsdk.utils import to_nano, bytes_to_b64str -from tonsdk.contract.wallet import Wallets, WalletVersionEnum +from tonsdk_ng.contract.token.ft import JettonMinter, JettonWallet +from tonsdk_ng.contract import Address +from tonsdk_ng.utils import to_nano, bytes_to_b64str +from tonsdk_ng.contract.wallet import Wallets, WalletVersionEnum def create_jetton_minter(): diff --git a/examples/tokens/jetton/transfer.py b/examples/tokens/jetton/transfer.py index 73b1f4c..458890f 100644 --- a/examples/tokens/jetton/transfer.py +++ b/examples/tokens/jetton/transfer.py @@ -1,6 +1,6 @@ -from tonsdk.contract.token.ft import JettonWallet -from tonsdk.utils import to_nano, bytes_to_b64str, Address -from tonsdk.contract.wallet import Wallets, WalletVersionEnum +from tonsdk_ng.contract.token.ft import JettonWallet +from tonsdk_ng.utils import to_nano, bytes_to_b64str, Address +from tonsdk_ng.contract.wallet import Wallets, WalletVersionEnum """your wallet mnemonics""" diff --git a/examples/tokens/nft/mint.py b/examples/tokens/nft/mint.py index 32066d0..2475f2d 100644 --- a/examples/tokens/nft/mint.py +++ b/examples/tokens/nft/mint.py @@ -1,7 +1,7 @@ -from tonsdk.contract.token.nft import NFTCollection, NFTItem -from tonsdk.contract import Address -from tonsdk.utils import to_nano, bytes_to_b64str -from tonsdk.contract.wallet import Wallets, WalletVersionEnum +from tonsdk_ng.contract.token.nft import NFTCollection, NFTItem +from tonsdk_ng.contract import Address +from tonsdk_ng.utils import to_nano, bytes_to_b64str +from tonsdk_ng.contract.wallet import Wallets, WalletVersionEnum def create_collection(): diff --git a/examples/tokens/nft/transfer.py b/examples/tokens/nft/transfer.py index 77c6978..7c3999c 100644 --- a/examples/tokens/nft/transfer.py +++ b/examples/tokens/nft/transfer.py @@ -1,6 +1,6 @@ -from tonsdk.contract.token.nft import NFTItem -from tonsdk.utils import to_nano, bytes_to_b64str, Address -from tonsdk.contract.wallet import Wallets, WalletVersionEnum +from tonsdk_ng.contract.token.nft import NFTItem +from tonsdk_ng.utils import to_nano, bytes_to_b64str, Address +from tonsdk_ng.contract.wallet import Wallets, WalletVersionEnum """your wallet mnemonics""" diff --git a/examples/types/cell.py b/examples/types/cell.py index 02ae92d..31da7e8 100644 --- a/examples/types/cell.py +++ b/examples/types/cell.py @@ -1,5 +1,5 @@ -from tonsdk.boc import begin_cell, Cell -from tonsdk.utils import Address +from tonsdk_ng.boc import begin_cell, Cell +from tonsdk_ng.utils import Address cell = begin_cell()\ .store_uint(4, 32)\ diff --git a/examples/types/slice.py b/examples/types/slice.py index f43715a..bf3b417 100644 --- a/examples/types/slice.py +++ b/examples/types/slice.py @@ -1,5 +1,5 @@ -from tonsdk.boc import Slice, begin_cell -from tonsdk.utils import Address +from tonsdk_ng.boc import Slice, begin_cell +from tonsdk_ng.utils import Address cell = begin_cell()\ .store_uint(4, 32)\ diff --git a/examples/wallets/highload.py b/examples/wallets/highload.py index 9dafdb5..8c233f6 100644 --- a/examples/wallets/highload.py +++ b/examples/wallets/highload.py @@ -1,5 +1,5 @@ -from tonsdk.contract.wallet import WalletVersionEnum, Wallets -from tonsdk.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano +from tonsdk_ng.contract.wallet import WalletVersionEnum, Wallets +from tonsdk_ng.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano mnemonics = [] _mnemonics, _pub_k, _priv_k, wallet = Wallets.from_mnemonics( diff --git a/examples/wallets/multisig/deploy.py b/examples/wallets/multisig/deploy.py index 9ea300c..80dd938 100644 --- a/examples/wallets/multisig/deploy.py +++ b/examples/wallets/multisig/deploy.py @@ -1,6 +1,6 @@ -from tonsdk.contract.wallet import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder -from tonsdk.crypto import mnemonic_new, mnemonic_to_wallet_key -from tonsdk.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano +from tonsdk_ng.contract.wallet import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder +from tonsdk_ng.crypto import mnemonic_new, mnemonic_to_wallet_key +from tonsdk_ng.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano """import or generate mnemonics""" diff --git a/examples/wallets/multisig/offchain_signatures.py b/examples/wallets/multisig/offchain_signatures.py index c875559..2da8593 100644 --- a/examples/wallets/multisig/offchain_signatures.py +++ b/examples/wallets/multisig/offchain_signatures.py @@ -1,6 +1,6 @@ -from tonsdk.contract.wallet import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder -from tonsdk.crypto import mnemonic_new, mnemonic_to_wallet_key -from tonsdk.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano +from tonsdk_ng.contract.wallet import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder +from tonsdk_ng.crypto import mnemonic_new, mnemonic_to_wallet_key +from tonsdk_ng.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano """import or generate mnemonics""" diff --git a/examples/wallets/multisig/onchain_signatures.py b/examples/wallets/multisig/onchain_signatures.py index be46a6a..c756412 100644 --- a/examples/wallets/multisig/onchain_signatures.py +++ b/examples/wallets/multisig/onchain_signatures.py @@ -1,6 +1,6 @@ -from tonsdk.contract.wallet import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder -from tonsdk.crypto import mnemonic_new, mnemonic_to_wallet_key, verify_sign -from tonsdk.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano, sign_message +from tonsdk_ng.contract.wallet import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder +from tonsdk_ng.crypto import mnemonic_new, mnemonic_to_wallet_key, verify_sign +from tonsdk_ng.utils import Address, bytes_to_b64str, b64str_to_bytes, to_nano, sign_message """import or generate mnemonics""" diff --git a/examples/wallets/wallet.py b/examples/wallets/wallet.py index 5f0d3e0..1030fec 100644 --- a/examples/wallets/wallet.py +++ b/examples/wallets/wallet.py @@ -1,6 +1,6 @@ -from tonsdk.crypto import mnemonic_new -from tonsdk.contract.wallet import Wallets, WalletVersionEnum -from tonsdk.utils import to_nano, bytes_to_b64str +from tonsdk_ng.crypto import mnemonic_new +from tonsdk_ng.contract.wallet import Wallets, WalletVersionEnum +from tonsdk_ng.utils import to_nano, bytes_to_b64str mnemonics = ['broken', 'decade', 'unit', 'bird', 'enrich', 'great', 'nurse', 'offer', 'rescue', diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..1a9d8e5 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1496 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "alabaster" +version = "0.7.16" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.9" +files = [ + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, +] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = true +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "argcomplete" +version = "3.2.3" +description = "Bash tab completion for argparse" +optional = false +python-versions = ">=3.8" +files = [ + {file = "argcomplete-3.2.3-py3-none-any.whl", hash = "sha256:c12355e0494c76a2a7b73e3a59b09024ca0ba1e279fb9ed6c1b82d5b74b6a70c"}, + {file = "argcomplete-3.2.3.tar.gz", hash = "sha256:bf7900329262e481be5a15f56f19736b376df6f82ed27576fa893652c5de6c23"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "bitarray" +version = "2.6.0" +description = "efficient arrays of booleans -- C extension" +optional = false +python-versions = "*" +files = [ + {file = "bitarray-2.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b080eb25811db46306dfce58b4760df32f40bcf5551ebba3b7c8d3ec90d9b988"}, + {file = "bitarray-2.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b0cfca1b5a57b540f4761b57de485196218733153c430d58f9e048e325c98b47"}, + {file = "bitarray-2.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6fa63a86aad0f45a27c7c5a27cd9b787fe9b1aed431f97f49ee8b834fa0780a0"}, + {file = "bitarray-2.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15d2a1c060a11fc5508715fef6177937614f9354dd3afe6a00e261775f8b0e8f"}, + {file = "bitarray-2.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ffc076a0e22cda949ccd062f37ecc3dc53856c6e8bdfe07e1e81c411cf31621"}, + {file = "bitarray-2.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecce266e24b21615a3ed234869be84bef492f6a34bb650d0e25dc3662c59bce4"}, + {file = "bitarray-2.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0399886ca8ead7d0f16f94545bda800467d6d9c63fbd4866ee7ede7981166ba8"}, + {file = "bitarray-2.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f263b18fdb8bf42cd7cf9849d5863847d215024c68fe74cf33bcd82641d4376a"}, + {file = "bitarray-2.6.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:119d503edf09bef37f2d0dc3b4a23c36c3c1e88e17701ab71388eb4780c046c7"}, + {file = "bitarray-2.6.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:985a937218aa3d1ac7013174bfcbb1cb2f3157e17c6e349e83386f33459be1c0"}, + {file = "bitarray-2.6.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d34673ebaf562347d004a465e16e2930c6568d196bb79d67fc6358f1213a1ac7"}, + {file = "bitarray-2.6.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7126563c86f6b60d87414124f035ff0d29de02ad9e46ea085de2c772b0be1331"}, + {file = "bitarray-2.6.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76c4e3261d6370383b02018cb964b5d9260e3c62dea31949910e9cc3a1c802d2"}, + {file = "bitarray-2.6.0-cp310-cp310-win32.whl", hash = "sha256:346d2c5452cc024c41d267ba99e48d38783c1706c50c4632a4484cc57b152d0e"}, + {file = "bitarray-2.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:b849a6cdd46608e7cc108c75e1265304e79488480a822bae7471e628f971a6f0"}, + {file = "bitarray-2.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d7bec01818c3a9d185f929cd36a82cc7acf13905920f7f595942105c5eef2300"}, + {file = "bitarray-2.6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a0bb91363041b45523e5bcbc4153a5e1eb1ddb21e46fe1910340c0d095e1a8e"}, + {file = "bitarray-2.6.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7ba4c964a36fe198a8c4b5d08924709d4ed0337b65ae222b6503ed3442a46e8"}, + {file = "bitarray-2.6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a239313e75da37d1f6548d666d4dd8554c4a92dabed15741612855d186e86e72"}, + {file = "bitarray-2.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9c492644f70f80f8266748c18309a0d73c22c47903f4b62f3fb772a15a8fd5f"}, + {file = "bitarray-2.6.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b756e5c771cdceb17622b6a0678fa78364e329d875de73a4f26bbacab8915a8"}, + {file = "bitarray-2.6.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c24d4a1b5baa46920b801aa55c0e0a640c6e7683a73a941302e102e2bd11a830"}, + {file = "bitarray-2.6.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f253b9bdf5abd039741a9594a681453c973b09dcb7edac9105961838675b7c6b"}, + {file = "bitarray-2.6.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:f4849709571b1a53669798d23cc8430e677dcf0eea88610a0412e1911233899a"}, + {file = "bitarray-2.6.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:67c5822f4bb6a419bc2f2dba9fa07b5646f0cda930bafa9e1130af6822e4bdf3"}, + {file = "bitarray-2.6.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6071d12043300e50a4b7ba9caeeca92aac567bb4ac4a227709e3c77a3d788587"}, + {file = "bitarray-2.6.0-cp36-cp36m-win32.whl", hash = "sha256:12c96dedd6e4584fecc2bf5fbffe1c635bd516eee7ade7b839c35aeba84336b4"}, + {file = "bitarray-2.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d53520b54206d8569b81eee56ccd9477af2f1b3ca355df9c48ee615a11e1a637"}, + {file = "bitarray-2.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ae3b8b48167579066a17c5ba1631d089f931f4eae8b4359ad123807d5e75c51"}, + {file = "bitarray-2.6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24331bd2f52cd5410e48c132f486ed02a4ca3b96133fb26e3a8f50a57c354be6"}, + {file = "bitarray-2.6.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:742d43cbbc7267caae6379e2156a1fd8532332920a3d919b68c2982d439a98ba"}, + {file = "bitarray-2.6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1479f533eaff4080078b6e5d06b467868bd6edd73bb6651a295bf662d40afa62"}, + {file = "bitarray-2.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec18a0b97ea6b912ea57dc00a3f8f3ce515d774d00951d30e2ae243589d3d021"}, + {file = "bitarray-2.6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6bd32e492cdc740ec36b6725457685c9f2aa012dd8cbdae1643fed2b6821895"}, + {file = "bitarray-2.6.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bfda0af4072df6e932ec510b72c461e1ec0ad0820a76df588cdfebf5a07f5b5d"}, + {file = "bitarray-2.6.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d523ffef1927cb686ad787b25b2e98a5bd53e3c40673c394f07bf9b281e69796"}, + {file = "bitarray-2.6.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b0e4a6f5360e5f6c3a2b250c9e9cd539a9aabf0465dbedbaf364203e74ff101b"}, + {file = "bitarray-2.6.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5bd315ac63b62de5eefbfa07969162ffbda8e535c3b7b3d41b565d2a88817b71"}, + {file = "bitarray-2.6.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d697cc38cb6fa9bae3b994dd3ce68552ffe69c453a3b6fd6a4f94bb8a8bfd70b"}, + {file = "bitarray-2.6.0-cp37-cp37m-win32.whl", hash = "sha256:c19e900b6f9df13c7f406f827c5643f83c0839a58d007b35a4d7df827601f740"}, + {file = "bitarray-2.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:878f16daa9c2062e4d29c1928b6f3eb50911726ad6d2006918a29ca6b38b5080"}, + {file = "bitarray-2.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:565c4334cb410f5eb62280dcfb3a52629e60ce430f31dfa4bbef92ec80de4890"}, + {file = "bitarray-2.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6d8ba8065d1b60da24d94078249cbf24a02d869d7dc9eba12db1fb513a375c79"}, + {file = "bitarray-2.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fc635b27939969d53cac53e8b8f860ea69fc98cc9867cac17dd193f41dc2a57f"}, + {file = "bitarray-2.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f853589426920d9bb3683f6b6cd11ce48d9d06a62c0b98ea4b82ebd8db3bddec"}, + {file = "bitarray-2.6.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:076a72531bcca99114036c3714bac8124f5529b60fb6a6986067c6f345238c76"}, + {file = "bitarray-2.6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:874a222ece2100b3a3a8f08c57da3267a4e2219d26474a46937552992fcec771"}, + {file = "bitarray-2.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6a4a4bf6fbc42b2674023ca58a47c86ee55c023a8af85420f266e86b10e7065"}, + {file = "bitarray-2.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f5df0377f3e7f1366e506c5295f08d3f8761e4a6381918931fc1d9594aa435e"}, + {file = "bitarray-2.6.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:42a071c9db755f267e5d3b9909ea8c22fb071d27860dd940facfacffbde79de8"}, + {file = "bitarray-2.6.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:36802129a3115023700c07725d981c74e23b0914551898f788e5a41aed2d63bf"}, + {file = "bitarray-2.6.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:c774328057a4b1fc48bee2dd5a60ee1e8e0ec112d29c4e6b9c550e1686b6db5c"}, + {file = "bitarray-2.6.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:763cac57692d07aa950b92c20f55ef66801955b71b4a1f4f48d5422d748c6dda"}, + {file = "bitarray-2.6.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:11996c4da9c1ca9f97143e939af75c5b24ad0fdc2fa13aeb0007ebfa3c602caf"}, + {file = "bitarray-2.6.0-cp38-cp38-win32.whl", hash = "sha256:3f238127789c993de937178c3ff836d0fad4f2da08af9f579668873ac1332a42"}, + {file = "bitarray-2.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:7f369872d551708d608e50a9ab8748d3d4f32a697dc5c2c37ff16cb8d7060210"}, + {file = "bitarray-2.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:049e8f017b5b6d1763ababa156ca5cbdea8a01e20a1e80525b0fbe9fb839d695"}, + {file = "bitarray-2.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:035d3e5ab3c1afa2cd88bbc33af595b4875a24b0d037dfef907b41bc4b0dbe2b"}, + {file = "bitarray-2.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:97609495479c5214c7b57173c17206ebb056507a8d26eebc17942d62f8f25944"}, + {file = "bitarray-2.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71cc3d1da4f682f27728745f21ed3447ee8f6a0019932126c422dd91278eb414"}, + {file = "bitarray-2.6.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c3d0a4a6061adc3d3128e1e1146940d17df8cbfe3d77cb66a1df69ddcdf27d5"}, + {file = "bitarray-2.6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c46c2ba24a517f391c3ab9e7a214185f95146d0b664b4b0463ab31e5387669f"}, + {file = "bitarray-2.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0302605b3bbc439083a400cf57d7464f1ac098c722309a03abaa7d97cd420b5"}, + {file = "bitarray-2.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d42fee0add2114e572b0cd6edefc4c52207874f58b70043f82faa8bb7141620"}, + {file = "bitarray-2.6.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5276c7247d350819d1dae385d8f78ebfb44ee90ff11a775f981d45cb366573e5"}, + {file = "bitarray-2.6.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e76642232db8330589ed1ac1cec0e9c3814c708521c336a5c79d39a5d8d8c206"}, + {file = "bitarray-2.6.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:1d0a2d896bcbcb5f32f60571ebd48349ec322dee5e137b342483108c5cbd0f03"}, + {file = "bitarray-2.6.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:8c811e59c86ce0a8515daf47db9c2484fd42e51bdb44581d7bcc9caad6c9a7a1"}, + {file = "bitarray-2.6.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:febaf00e498230ce2e75dac910056f0e3a91c8631b7ceb6385bb39d448960bc5"}, + {file = "bitarray-2.6.0-cp39-cp39-win32.whl", hash = "sha256:2cfe1661b614314d67e6884e5e19e36957ff6faea5fcea7f25840dff95288248"}, + {file = "bitarray-2.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f37b5282b029d9f51454f8c580eb6a24e5dc140ef5866290afb20e607d2dce5f"}, + {file = "bitarray-2.6.0.tar.gz", hash = "sha256:56d3f16dd807b1c56732a244ce071c135ee973d3edc9929418c1b24c5439a0fd"}, +] + +[[package]] +name = "black" +version = "24.4.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, + {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, + {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, + {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, + {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, + {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, + {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, + {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, + {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, + {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, + {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, + {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, + {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, + {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, + {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, + {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, + {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, + {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, + {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, + {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, + {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, + {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +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)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.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"}, +] + +[[package]] +name = "commitizen" +version = "3.22.0" +description = "Python commitizen client tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "commitizen-3.22.0-py3-none-any.whl", hash = "sha256:671a587a8684220e7527e7b6a79374d2b13dda7f287a7ef78be6c4ea9919520f"}, + {file = "commitizen-3.22.0.tar.gz", hash = "sha256:806141c59ed9151acd468e6579c76df83a1536dae27a5f7be7b2ffb1847b79d5"}, +] + +[package.dependencies] +argcomplete = ">=1.12.1,<3.3" +charset-normalizer = ">=2.1.0,<4" +colorama = ">=0.4.1,<0.5.0" +decli = ">=0.6.0,<0.7.0" +importlib_metadata = ">=4.13,<8" +jinja2 = ">=2.10.3" +packaging = ">=19" +pyyaml = ">=3.08" +questionary = ">=2.0,<3.0" +termcolor = ">=1.1,<3" +tomlkit = ">=0.5.3,<1.0.0" + +[[package]] +name = "coverage" +version = "7.4.4" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "decli" +version = "0.6.1" +description = "Minimal, easy-to-use, declarative cli tool" +optional = false +python-versions = ">=3.7" +files = [ + {file = "decli-0.6.1-py3-none-any.whl", hash = "sha256:7815ac58617764e1a200d7cadac6315fcaacc24d727d182f9878dd6378ccf869"}, + {file = "decli-0.6.1.tar.gz", hash = "sha256:ed88ccb947701e8e5509b7945fda56e150e2ac74a69f25d47ac85ef30ab0c0f0"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "docutils" +version = "0.21.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +files = [ + {file = "docutils-0.21.1-py3-none-any.whl", hash = "sha256:14c8d34a55b46c88f9f714adb29cefbdd69fb82f3fef825e59c5faab935390d8"}, + {file = "docutils-0.21.1.tar.gz", hash = "sha256:65249d8a5345bc95e0f40f280ba63c98eb24de35c6c8f5b662e3e8948adea83f"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.13.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "furo" +version = "2024.1.29" +description = "A clean customisable Sphinx documentation theme." +optional = false +python-versions = ">=3.8" +files = [ + {file = "furo-2024.1.29-py3-none-any.whl", hash = "sha256:3548be2cef45a32f8cdc0272d415fcb3e5fa6a0eb4ddfe21df3ecf1fe45a13cf"}, + {file = "furo-2024.1.29.tar.gz", hash = "sha256:4d6b2fe3f10a6e36eb9cc24c1e7beb38d7a23fc7b3c382867503b7fcac8a1e02"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=6.0,<8.0" +sphinx-basic-ng = "*" + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = true +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = true +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = true +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "identify" +version = "2.5.35" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "7.1.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mypy" +version = "1.9.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +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"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.7.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, + {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.36" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, + {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "questionary" +version = "2.0.1" +description = "Python library to build pretty command line user prompts ⭐️" +optional = false +python-versions = ">=3.8" +files = [ + {file = "questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2"}, + {file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"}, +] + +[package.dependencies] +prompt_toolkit = ">=2.0,<=3.0.36" + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruff" +version = "0.3.7" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, +] + +[[package]] +name = "setuptools" +version = "69.5.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = true +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "sphinx" +version = "7.3.6" +description = "Python documentation generator" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx-7.3.6-py3-none-any.whl", hash = "sha256:d6c09acd42094fcd96a9299c1b32b2dafe82d667fdd6e532e5978443ad074c2a"}, + {file = "sphinx-7.3.6.tar.gz", hash = "sha256:fc9f3d13fed5c9a0e677d368090e209899ce5d0081eb552b657e2923e57517f0"}, +] + +[package.dependencies] +alabaster = ">=0.7.14,<0.8.0" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.22" +imagesize = ">=1.3" +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.14" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.9" +tomli = {version = ">=2", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +description = "A modern skeleton for Sphinx themes." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.8" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.6" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.5" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.7" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.10" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "termcolor" +version = "2.4.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.8" +files = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.25.3" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.3-py3-none-any.whl", hash = "sha256:8aac4332f2ea6ef519c648d0bc48a5b1d324994753519919bddbb1aff25a104e"}, + {file = "virtualenv-20.25.3.tar.gz", hash = "sha256:7bb554bbdfeaacc3349fa614ea5bff6ac300fc7c335e9facf3a3bcfc703f45be"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[extras] +full = ["httpx"] +http-api = ["httpx"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.10" +content-hash = "c4dcf7b1ba8cd8f2c07d00a0197b508bb9f1315be8e471bed9fbb6c41dd3d38e" diff --git a/pyproject.toml b/pyproject.toml index 5f8f5e3..82ac857 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,124 @@ +[tool.poetry] +name = "tonsdk_ng" +version = "0.1.0" +description = "Python SDK for TON" +authors = ["tonfactory.org"] +license = "Apache-2.0" +keywords = ["TON", "TON SDK", "TON utils", "TON tools"] +readme = "README.md" +homepage = "https://github.com/gtors/tonsdk_ng" +classifiers = [ + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Adaptive Technologies", +] + +[tool.poetry.dependencies] +python = ">=3.10" +pynacl = { version = ">=1.4.0" } +bitarray = { version = "==2.6.0" } +httpx = { version = "*", optional = true } + +[tool.poetry.extras] +http_api = ["httpx"] +full = ["httpx"] + +[tool.poetry.group.dev.dependencies] +black = "*" +commitizen = "*" +furo = "*" +isort = "*" +mypy = "*" +pre-commit = "*" +pytest = "*" +pytest-cov = "*" +ruff = "*" + + [build-system] -build-backend = "setuptools.build_meta" -requires = ["setuptools", "wheel"] \ No newline at end of file +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.commitizen] +name = "cz_conventional_commits" +version = "0.1.0" +tag_format = "$version" + +[tool.black] +target-version = ["py310", "py311"] +line-length = 80 + +[tool.ruff] +line-length = 80 +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".hypothesis", + ".pants.d", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +target-version = "py310" + +[tool.ruff.per-file-ignores] +"__init__.py" = ["F401", "F403"] + +[tool.ruff.lint] +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + # "UP", + # flake8-bugbear + "B", + # flake8-simplify + "SIM", + # isort + # "I", +] +ignore = [ + # star imports + "F405", "F403", + # lambda expression assignment + "E731", + # Loop control variable not used within loop body + "B007", +] + +[tool.mypy] +python_version = "3.10" + +ignore_missing_imports = true +check_untyped_defs = true + +[tool.isort] +profile = "black" +py_version=310 +line_length = 80 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 994256e..0000000 --- a/setup.cfg +++ /dev/null @@ -1,46 +0,0 @@ -[metadata] -name = tonsdk -version = 1.0.13 -description = Python SDK for TON -long_description = file: README.md -long_description_content_type = text/markdown -keywords = TON, TON SDK, TON utils, TON tools -license = Apache 2.0 -classifiers = - Programming Language :: Python :: 3 - Environment :: Console - Environment :: Web Environment - Intended Audience :: Developers - Intended Audience :: End Users/Desktop - License :: OSI Approved :: Apache Software License - Natural Language :: English - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3 :: Only - Topic :: Adaptive Technologies -author = tonfactory.org -project_urls = - Github = https://github.com/tonfactory/tonsdk - -[options] -zip_safe = False -include_package_data = True -packages = find: -python_requires = >=3.7 -install_requires = - pynacl>=1.4.0 - bitarray==2.6.0 - -[options.packages.find] -exclude = - examples* - devtools* - docs* - tests* - venv* diff --git a/setup.py b/setup.py deleted file mode 100644 index fc1f76c..0000000 --- a/setup.py +++ /dev/null @@ -1,3 +0,0 @@ -from setuptools import setup - -setup() \ No newline at end of file diff --git a/tonsdk/crypto/__init__.py b/tonsdk/crypto/__init__.py deleted file mode 100644 index 4641679..0000000 --- a/tonsdk/crypto/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from ._keystore import generate_new_keystore, generate_keystore_key -from ._mnemonic import mnemonic_new, mnemonic_to_wallet_key, mnemonic_is_valid -from ._utils import private_key_to_public_key, verify_sign -__all__ = [ - 'mnemonic_new', - 'mnemonic_to_wallet_key', - 'mnemonic_is_valid', - - 'generate_new_keystore', - 'generate_keystore_key', - 'private_key_to_public_key', - 'verify_sign', - 'generate_key_pair', -] diff --git a/tonsdk/crypto/bip39/_english.py b/tonsdk/crypto/bip39/_english.py deleted file mode 100644 index 7cb77da..0000000 --- a/tonsdk/crypto/bip39/_english.py +++ /dev/null @@ -1 +0,0 @@ -words = ['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid', 'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual', 'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance', 'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'age', 'agent', 'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album', 'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone', 'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among', 'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry', 'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique', 'anxiety', 'any', 'apart', 'apology', 'appear', 'apple', 'approve', 'april', 'arch', 'arctic', 'area', 'arena', 'argue', 'arm', 'armed', 'armor', 'army', 'around', 'arrange', 'arrest', 'arrive', 'arrow', 'art', 'artefact', 'artist', 'artwork', 'ask', 'aspect', 'assault', 'asset', 'assist', 'assume', 'asthma', 'athlete', 'atom', 'attack', 'attend', 'attitude', 'attract', 'auction', 'audit', 'august', 'aunt', 'author', 'auto', 'autumn', 'average', 'avocado', 'avoid', 'awake', 'aware', 'away', 'awesome', 'awful', 'awkward', 'axis', 'baby', 'bachelor', 'bacon', 'badge', 'bag', 'balance', 'balcony', 'ball', 'bamboo', 'banana', 'banner', 'bar', 'barely', 'bargain', 'barrel', 'base', 'basic', 'basket', 'battle', 'beach', 'bean', 'beauty', 'because', 'become', 'beef', 'before', 'begin', 'behave', 'behind', 'believe', 'below', 'belt', 'bench', 'benefit', 'best', 'betray', 'better', 'between', 'beyond', 'bicycle', 'bid', 'bike', 'bind', 'biology', 'bird', 'birth', 'bitter', 'black', 'blade', 'blame', 'blanket', 'blast', 'bleak', 'bless', 'blind', 'blood', 'blossom', 'blouse', 'blue', 'blur', 'blush', 'board', 'boat', 'body', 'boil', 'bomb', 'bone', 'bonus', 'book', 'boost', 'border', 'boring', 'borrow', 'boss', 'bottom', 'bounce', 'box', 'boy', 'bracket', 'brain', 'brand', 'brass', 'brave', 'bread', 'breeze', 'brick', 'bridge', 'brief', 'bright', 'bring', 'brisk', 'broccoli', 'broken', 'bronze', 'broom', 'brother', 'brown', 'brush', 'bubble', 'buddy', 'budget', 'buffalo', 'build', 'bulb', 'bulk', 'bullet', 'bundle', 'bunker', 'burden', 'burger', 'burst', 'bus', 'business', 'busy', 'butter', 'buyer', 'buzz', 'cabbage', 'cabin', 'cable', 'cactus', 'cage', 'cake', 'call', 'calm', 'camera', 'camp', 'can', 'canal', 'cancel', 'candy', 'cannon', 'canoe', 'canvas', 'canyon', 'capable', 'capital', 'captain', 'car', 'carbon', 'card', 'cargo', 'carpet', 'carry', 'cart', 'case', 'cash', 'casino', 'castle', 'casual', 'cat', 'catalog', 'catch', 'category', 'cattle', 'caught', 'cause', 'caution', 'cave', 'ceiling', 'celery', 'cement', 'census', 'century', 'cereal', 'certain', 'chair', 'chalk', 'champion', 'change', 'chaos', 'chapter', 'charge', 'chase', 'chat', 'cheap', 'check', 'cheese', 'chef', 'cherry', 'chest', 'chicken', 'chief', 'child', 'chimney', 'choice', 'choose', 'chronic', 'chuckle', 'chunk', 'churn', 'cigar', 'cinnamon', 'circle', 'citizen', 'city', 'civil', 'claim', 'clap', 'clarify', 'claw', 'clay', 'clean', 'clerk', 'clever', 'click', 'client', 'cliff', 'climb', 'clinic', 'clip', 'clock', 'clog', 'close', 'cloth', 'cloud', 'clown', 'club', 'clump', 'cluster', 'clutch', 'coach', 'coast', 'coconut', 'code', 'coffee', 'coil', 'coin', 'collect', 'color', 'column', 'combine', 'come', 'comfort', 'comic', 'common', 'company', 'concert', 'conduct', 'confirm', 'congress', 'connect', 'consider', 'control', 'convince', 'cook', 'cool', 'copper', 'copy', 'coral', 'core', 'corn', 'correct', 'cost', 'cotton', 'couch', 'country', 'couple', 'course', 'cousin', 'cover', 'coyote', 'crack', 'cradle', 'craft', 'cram', 'crane', 'crash', 'crater', 'crawl', 'crazy', 'cream', 'credit', 'creek', 'crew', 'cricket', 'crime', 'crisp', 'critic', 'crop', 'cross', 'crouch', 'crowd', 'crucial', 'cruel', 'cruise', 'crumble', 'crunch', 'crush', 'cry', 'crystal', 'cube', 'culture', 'cup', 'cupboard', 'curious', 'current', 'curtain', 'curve', 'cushion', 'custom', 'cute', 'cycle', 'dad', 'damage', 'damp', 'dance', 'danger', 'daring', 'dash', 'daughter', 'dawn', 'day', 'deal', 'debate', 'debris', 'decade', 'december', 'decide', 'decline', 'decorate', 'decrease', 'deer', 'defense', 'define', 'defy', 'degree', 'delay', 'deliver', 'demand', 'demise', 'denial', 'dentist', 'deny', 'depart', 'depend', 'deposit', 'depth', 'deputy', 'derive', 'describe', 'desert', 'design', 'desk', 'despair', 'destroy', 'detail', 'detect', 'develop', 'device', 'devote', 'diagram', 'dial', 'diamond', 'diary', 'dice', 'diesel', 'diet', 'differ', 'digital', 'dignity', 'dilemma', 'dinner', 'dinosaur', 'direct', 'dirt', 'disagree', 'discover', 'disease', 'dish', 'dismiss', 'disorder', 'display', 'distance', 'divert', 'divide', 'divorce', 'dizzy', 'doctor', 'document', 'dog', 'doll', 'dolphin', 'domain', 'donate', 'donkey', 'donor', 'door', 'dose', 'double', 'dove', 'draft', 'dragon', 'drama', 'drastic', 'draw', 'dream', 'dress', 'drift', 'drill', 'drink', 'drip', 'drive', 'drop', 'drum', 'dry', 'duck', 'dumb', 'dune', 'during', 'dust', 'dutch', 'duty', 'dwarf', 'dynamic', 'eager', 'eagle', 'early', 'earn', 'earth', 'easily', 'east', 'easy', 'echo', 'ecology', 'economy', 'edge', 'edit', 'educate', 'effort', 'egg', 'eight', 'either', 'elbow', 'elder', 'electric', 'elegant', 'element', 'elephant', 'elevator', 'elite', 'else', 'embark', 'embody', 'embrace', 'emerge', 'emotion', 'employ', 'empower', 'empty', 'enable', 'enact', 'end', 'endless', 'endorse', 'enemy', 'energy', 'enforce', 'engage', 'engine', 'enhance', 'enjoy', 'enlist', 'enough', 'enrich', 'enroll', 'ensure', 'enter', 'entire', 'entry', 'envelope', 'episode', 'equal', 'equip', 'era', 'erase', 'erode', 'erosion', 'error', 'erupt', 'escape', 'essay', 'essence', 'estate', 'eternal', 'ethics', 'evidence', 'evil', 'evoke', 'evolve', 'exact', 'example', 'excess', 'exchange', 'excite', 'exclude', 'excuse', 'execute', 'exercise', 'exhaust', 'exhibit', 'exile', 'exist', 'exit', 'exotic', 'expand', 'expect', 'expire', 'explain', 'expose', 'express', 'extend', 'extra', 'eye', 'eyebrow', 'fabric', 'face', 'faculty', 'fade', 'faint', 'faith', 'fall', 'false', 'fame', 'family', 'famous', 'fan', 'fancy', 'fantasy', 'farm', 'fashion', 'fat', 'fatal', 'father', 'fatigue', 'fault', 'favorite', 'feature', 'february', 'federal', 'fee', 'feed', 'feel', 'female', 'fence', 'festival', 'fetch', 'fever', 'few', 'fiber', 'fiction', 'field', 'figure', 'file', 'film', 'filter', 'final', 'find', 'fine', 'finger', 'finish', 'fire', 'firm', 'first', 'fiscal', 'fish', 'fit', 'fitness', 'fix', 'flag', 'flame', 'flash', 'flat', 'flavor', 'flee', 'flight', 'flip', 'float', 'flock', 'floor', 'flower', 'fluid', 'flush', 'fly', 'foam', 'focus', 'fog', 'foil', 'fold', 'follow', 'food', 'foot', 'force', 'forest', 'forget', 'fork', 'fortune', 'forum', 'forward', 'fossil', 'foster', 'found', 'fox', 'fragile', 'frame', 'frequent', 'fresh', 'friend', 'fringe', 'frog', 'front', 'frost', 'frown', 'frozen', 'fruit', 'fuel', 'fun', 'funny', 'furnace', 'fury', 'future', 'gadget', 'gain', 'galaxy', 'gallery', 'game', 'gap', 'garage', 'garbage', 'garden', 'garlic', 'garment', 'gas', 'gasp', 'gate', 'gather', 'gauge', 'gaze', 'general', 'genius', 'genre', 'gentle', 'genuine', 'gesture', 'ghost', 'giant', 'gift', 'giggle', 'ginger', 'giraffe', 'girl', 'give', 'glad', 'glance', 'glare', 'glass', 'glide', 'glimpse', 'globe', 'gloom', 'glory', 'glove', 'glow', 'glue', 'goat', 'goddess', 'gold', 'good', 'goose', 'gorilla', 'gospel', 'gossip', 'govern', 'gown', 'grab', 'grace', 'grain', 'grant', 'grape', 'grass', 'gravity', 'great', 'green', 'grid', 'grief', 'grit', 'grocery', 'group', 'grow', 'grunt', 'guard', 'guess', 'guide', 'guilt', 'guitar', 'gun', 'gym', 'habit', 'hair', 'half', 'hammer', 'hamster', 'hand', 'happy', 'harbor', 'hard', 'harsh', 'harvest', 'hat', 'have', 'hawk', 'hazard', 'head', 'health', 'heart', 'heavy', 'hedgehog', 'height', 'hello', 'helmet', 'help', 'hen', 'hero', 'hidden', 'high', 'hill', 'hint', 'hip', 'hire', 'history', 'hobby', 'hockey', 'hold', 'hole', 'holiday', 'hollow', 'home', 'honey', 'hood', 'hope', 'horn', 'horror', 'horse', 'hospital', 'host', 'hotel', 'hour', 'hover', 'hub', 'huge', 'human', 'humble', 'humor', 'hundred', 'hungry', 'hunt', 'hurdle', 'hurry', 'hurt', 'husband', 'hybrid', 'ice', 'icon', 'idea', 'identify', 'idle', 'ignore', 'ill', 'illegal', 'illness', 'image', 'imitate', 'immense', 'immune', 'impact', 'impose', 'improve', 'impulse', 'inch', 'include', 'income', 'increase', 'index', 'indicate', 'indoor', 'industry', 'infant', 'inflict', 'inform', 'inhale', 'inherit', 'initial', 'inject', 'injury', 'inmate', 'inner', 'innocent', 'input', 'inquiry', 'insane', 'insect', 'inside', 'inspire', 'install', 'intact', 'interest', 'into', 'invest', 'invite', 'involve', 'iron', 'island', 'isolate', 'issue', 'item', 'ivory', 'jacket', 'jaguar', 'jar', 'jazz', 'jealous', 'jeans', 'jelly', 'jewel', 'job', 'join', 'joke', 'journey', 'joy', 'judge', 'juice', 'jump', 'jungle', 'junior', 'junk', 'just', 'kangaroo', 'keen', 'keep', 'ketchup', 'key', 'kick', 'kid', 'kidney', 'kind', 'kingdom', 'kiss', 'kit', 'kitchen', 'kite', 'kitten', 'kiwi', 'knee', 'knife', 'knock', 'know', 'lab', 'label', 'labor', 'ladder', 'lady', 'lake', 'lamp', 'language', 'laptop', 'large', 'later', 'latin', 'laugh', 'laundry', 'lava', 'law', 'lawn', 'lawsuit', 'layer', 'lazy', 'leader', 'leaf', 'learn', 'leave', 'lecture', 'left', 'leg', 'legal', 'legend', 'leisure', 'lemon', 'lend', 'length', 'lens', 'leopard', 'lesson', 'letter', 'level', 'liar', 'liberty', 'library', 'license', 'life', 'lift', 'light', 'like', 'limb', 'limit', 'link', 'lion', 'liquid', 'list', 'little', 'live', 'lizard', 'load', 'loan', 'lobster', 'local', 'lock', 'logic', 'lonely', 'long', 'loop', 'lottery', 'loud', 'lounge', 'love', 'loyal', 'lucky', 'luggage', 'lumber', 'lunar', 'lunch', 'luxury', 'lyrics', 'machine', 'mad', 'magic', 'magnet', 'maid', 'mail', 'main', 'major', 'make', 'mammal', 'man', 'manage', 'mandate', 'mango', 'mansion', 'manual', 'maple', 'marble', 'march', 'margin', 'marine', 'market', 'marriage', 'mask', 'mass', 'master', 'match', 'material', 'math', 'matrix', 'matter', 'maximum', 'maze', 'meadow', 'mean', 'measure', 'meat', 'mechanic', 'medal', 'media', 'melody', 'melt', 'member', 'memory', 'mention', 'menu', 'mercy', 'merge', 'merit', 'merry', 'mesh', 'message', 'metal', 'method', 'middle', 'midnight', 'milk', 'million', 'mimic', 'mind', 'minimum', 'minor', 'minute', 'miracle', 'mirror', 'misery', 'miss', 'mistake', 'mix', 'mixed', 'mixture', 'mobile', 'model', 'modify', 'mom', 'moment', 'monitor', 'monkey', 'monster', 'month', 'moon', 'moral', 'more', 'morning', 'mosquito', 'mother', 'motion', 'motor', 'mountain', 'mouse', 'move', 'movie', 'much', 'muffin', 'mule', 'multiply', 'muscle', 'museum', 'mushroom', 'music', 'must', 'mutual', 'myself', 'mystery', 'myth', 'naive', 'name', 'napkin', 'narrow', 'nasty', 'nation', 'nature', 'near', 'neck', 'need', 'negative', 'neglect', 'neither', 'nephew', 'nerve', 'nest', 'net', 'network', 'neutral', 'never', 'news', 'next', 'nice', 'night', 'noble', 'noise', 'nominee', 'noodle', 'normal', 'north', 'nose', 'notable', 'note', 'nothing', 'notice', 'novel', 'now', 'nuclear', 'number', 'nurse', 'nut', 'oak', 'obey', 'object', 'oblige', 'obscure', 'observe', 'obtain', 'obvious', 'occur', 'ocean', 'october', 'odor', 'off', 'offer', 'office', 'often', 'oil', 'okay', 'old', 'olive', 'olympic', 'omit', 'once', 'one', 'onion', 'online', 'only', 'open', 'opera', 'opinion', 'oppose', 'option', 'orange', 'orbit', 'orchard', 'order', 'ordinary', 'organ', 'orient', 'original', 'orphan', 'ostrich', 'other', 'outdoor', 'outer', 'output', 'outside', 'oval', 'oven', 'over', 'own', 'owner', 'oxygen', 'oyster', 'ozone', 'pact', 'paddle', 'page', 'pair', 'palace', 'palm', 'panda', 'panel', 'panic', 'panther', 'paper', 'parade', 'parent', 'park', 'parrot', 'party', 'pass', 'patch', 'path', 'patient', 'patrol', 'pattern', 'pause', 'pave', 'payment', 'peace', 'peanut', 'pear', 'peasant', 'pelican', 'pen', 'penalty', 'pencil', 'people', 'pepper', 'perfect', 'permit', 'person', 'pet', 'phone', 'photo', 'phrase', 'physical', 'piano', 'picnic', 'picture', 'piece', 'pig', 'pigeon', 'pill', 'pilot', 'pink', 'pioneer', 'pipe', 'pistol', 'pitch', 'pizza', 'place', 'planet', 'plastic', 'plate', 'play', 'please', 'pledge', 'pluck', 'plug', 'plunge', 'poem', 'poet', 'point', 'polar', 'pole', 'police', 'pond', 'pony', 'pool', 'popular', 'portion', 'position', 'possible', 'post', 'potato', 'pottery', 'poverty', 'powder', 'power', 'practice', 'praise', 'predict', 'prefer', 'prepare', 'present', 'pretty', 'prevent', 'price', 'pride', 'primary', 'print', 'priority', 'prison', 'private', 'prize', 'problem', 'process', 'produce', 'profit', 'program', 'project', 'promote', 'proof', 'property', 'prosper', 'protect', 'proud', 'provide', 'public', 'pudding', 'pull', 'pulp', 'pulse', 'pumpkin', 'punch', 'pupil', 'puppy', 'purchase', 'purity', 'purpose', 'purse', 'push', 'put', 'puzzle', 'pyramid', 'quality', 'quantum', 'quarter', 'question', 'quick', 'quit', 'quiz', 'quote', 'rabbit', 'raccoon', 'race', 'rack', 'radar', 'radio', 'rail', 'rain', 'raise', 'rally', 'ramp', 'ranch', 'random', 'range', 'rapid', 'rare', 'rate', 'rather', 'raven', 'raw', 'razor', 'ready', 'real', 'reason', 'rebel', 'rebuild', 'recall', 'receive', 'recipe', 'record', 'recycle', 'reduce', 'reflect', 'reform', 'refuse', 'region', 'regret', 'regular', 'reject', 'relax', 'release', 'relief', 'rely', 'remain', 'remember', 'remind', 'remove', 'render', 'renew', 'rent', 'reopen', 'repair', 'repeat', 'replace', 'report', 'require', 'rescue', 'resemble', 'resist', 'resource', 'response', 'result', 'retire', 'retreat', 'return', 'reunion', 'reveal', 'review', 'reward', 'rhythm', 'rib', 'ribbon', 'rice', 'rich', 'ride', 'ridge', 'rifle', 'right', 'rigid', 'ring', 'riot', 'ripple', 'risk', 'ritual', 'rival', 'river', 'road', 'roast', 'robot', 'robust', 'rocket', 'romance', 'roof', 'rookie', 'room', 'rose', 'rotate', 'rough', 'round', 'route', 'royal', 'rubber', 'rude', 'rug', 'rule', 'run', 'runway', 'rural', 'sad', 'saddle', 'sadness', 'safe', 'sail', 'salad', 'salmon', 'salon', 'salt', 'salute', 'same', 'sample', 'sand', 'satisfy', 'satoshi', 'sauce', 'sausage', 'save', 'say', 'scale', 'scan', 'scare', 'scatter', 'scene', 'scheme', 'school', 'science', 'scissors', 'scorpion', 'scout', 'scrap', 'screen', 'script', 'scrub', 'sea', 'search', 'season', 'seat', 'second', 'secret', 'section', 'security', 'seed', 'seek', 'segment', 'select', 'sell', 'seminar', 'senior', 'sense', 'sentence', 'series', 'service', 'session', 'settle', 'setup', 'seven', 'shadow', 'shaft', 'shallow', 'share', 'shed', 'shell', 'sheriff', 'shield', 'shift', 'shine', 'ship', 'shiver', 'shock', 'shoe', 'shoot', 'shop', 'short', 'shoulder', 'shove', 'shrimp', 'shrug', 'shuffle', 'shy', 'sibling', 'sick', 'side', 'siege', 'sight', 'sign', 'silent', 'silk', 'silly', 'silver', 'similar', 'simple', 'since', 'sing', 'siren', 'sister', 'situate', 'six', 'size', 'skate', 'sketch', 'ski', 'skill', 'skin', 'skirt', 'skull', 'slab', 'slam', 'sleep', 'slender', 'slice', 'slide', 'slight', 'slim', 'slogan', 'slot', 'slow', 'slush', 'small', 'smart', 'smile', 'smoke', 'smooth', 'snack', 'snake', 'snap', 'sniff', 'snow', 'soap', 'soccer', 'social', 'sock', 'soda', 'soft', 'solar', 'soldier', 'solid', 'solution', 'solve', 'someone', 'song', 'soon', 'sorry', 'sort', 'soul', 'sound', 'soup', 'source', 'south', 'space', 'spare', 'spatial', 'spawn', 'speak', 'special', 'speed', 'spell', 'spend', 'sphere', 'spice', 'spider', 'spike', 'spin', 'spirit', 'split', 'spoil', 'sponsor', 'spoon', 'sport', 'spot', 'spray', 'spread', 'spring', 'spy', 'square', 'squeeze', 'squirrel', 'stable', 'stadium', 'staff', 'stage', 'stairs', 'stamp', 'stand', 'start', 'state', 'stay', 'steak', 'steel', 'stem', 'step', 'stereo', 'stick', 'still', 'sting', 'stock', 'stomach', 'stone', 'stool', 'story', 'stove', 'strategy', 'street', 'strike', 'strong', 'struggle', 'student', 'stuff', 'stumble', 'style', 'subject', 'submit', 'subway', 'success', 'such', 'sudden', 'suffer', 'sugar', 'suggest', 'suit', 'summer', 'sun', 'sunny', 'sunset', 'super', 'supply', 'supreme', 'sure', 'surface', 'surge', 'surprise', 'surround', 'survey', 'suspect', 'sustain', 'swallow', 'swamp', 'swap', 'swarm', 'swear', 'sweet', 'swift', 'swim', 'swing', 'switch', 'sword', 'symbol', 'symptom', 'syrup', 'system', 'table', 'tackle', 'tag', 'tail', 'talent', 'talk', 'tank', 'tape', 'target', 'task', 'taste', 'tattoo', 'taxi', 'teach', 'team', 'tell', 'ten', 'tenant', 'tennis', 'tent', 'term', 'test', 'text', 'thank', 'that', 'theme', 'then', 'theory', 'there', 'they', 'thing', 'this', 'thought', 'three', 'thrive', 'throw', 'thumb', 'thunder', 'ticket', 'tide', 'tiger', 'tilt', 'timber', 'time', 'tiny', 'tip', 'tired', 'tissue', 'title', 'toast', 'tobacco', 'today', 'toddler', 'toe', 'together', 'toilet', 'token', 'tomato', 'tomorrow', 'tone', 'tongue', 'tonight', 'tool', 'tooth', 'top', 'topic', 'topple', 'torch', 'tornado', 'tortoise', 'toss', 'total', 'tourist', 'toward', 'tower', 'town', 'toy', 'track', 'trade', 'traffic', 'tragic', 'train', 'transfer', 'trap', 'trash', 'travel', 'tray', 'treat', 'tree', 'trend', 'trial', 'tribe', 'trick', 'trigger', 'trim', 'trip', 'trophy', 'trouble', 'truck', 'true', 'truly', 'trumpet', 'trust', 'truth', 'try', 'tube', 'tuition', 'tumble', 'tuna', 'tunnel', 'turkey', 'turn', 'turtle', 'twelve', 'twenty', 'twice', 'twin', 'twist', 'two', 'type', 'typical', 'ugly', 'umbrella', 'unable', 'unaware', 'uncle', 'uncover', 'under', 'undo', 'unfair', 'unfold', 'unhappy', 'uniform', 'unique', 'unit', 'universe', 'unknown', 'unlock', 'until', 'unusual', 'unveil', 'update', 'upgrade', 'uphold', 'upon', 'upper', 'upset', 'urban', 'urge', 'usage', 'use', 'used', 'useful', 'useless', 'usual', 'utility', 'vacant', 'vacuum', 'vague', 'valid', 'valley', 'valve', 'van', 'vanish', 'vapor', 'various', 'vast', 'vault', 'vehicle', 'velvet', 'vendor', 'venture', 'venue', 'verb', 'verify', 'version', 'very', 'vessel', 'veteran', 'viable', 'vibrant', 'vicious', 'victory', 'video', 'view', 'village', 'vintage', 'violin', 'virtual', 'virus', 'visa', 'visit', 'visual', 'vital', 'vivid', 'vocal', 'voice', 'void', 'volcano', 'volume', 'vote', 'voyage', 'wage', 'wagon', 'wait', 'walk', 'wall', 'walnut', 'want', 'warfare', 'warm', 'warrior', 'wash', 'wasp', 'waste', 'water', 'wave', 'way', 'wealth', 'weapon', 'wear', 'weasel', 'weather', 'web', 'wedding', 'weekend', 'weird', 'welcome', 'west', 'wet', 'whale', 'what', 'wheat', 'wheel', 'when', 'where', 'whip', 'whisper', 'wide', 'width', 'wife', 'wild', 'will', 'win', 'window', 'wine', 'wing', 'wink', 'winner', 'winter', 'wire', 'wisdom', 'wise', 'wish', 'witness', 'wolf', 'woman', 'wonder', 'wood', 'wool', 'word', 'work', 'world', 'worry', 'worth', 'wrap', 'wreck', 'wrestle', 'wrist', 'write', 'wrong', 'yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo'] \ No newline at end of file diff --git a/tonsdk/provider/__init__.py b/tonsdk/provider/__init__.py deleted file mode 100644 index a8c8740..0000000 --- a/tonsdk/provider/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from ._address import prepare_address, address_state -from ._exceptions import ResponseError -from ._toncenter import ToncenterClient, ToncenterWrongResult -from ._tonlibjson import AsyncTonlibClient, SyncTonlibClient, TonLibWrongResult -from ._utils import parse_response - -all = [ - 'AsyncTonlibClient', - 'SyncTonlibClient', - 'ToncenterClient', - - 'prepare_address', - 'address_state', - - 'parse_response', - - 'ResponseError', - 'TonLibWrongResult', - 'ToncenterWrongResult', -] diff --git a/tonsdk/provider/_tonlibjson/_async/_client.py b/tonsdk/provider/_tonlibjson/_async/_client.py deleted file mode 100644 index a547274..0000000 --- a/tonsdk/provider/_tonlibjson/_async/_client.py +++ /dev/null @@ -1,721 +0,0 @@ -import asyncio -import codecs -import json -import logging -import random -from copy import deepcopy -from pathlib import Path - -from tvm_valuetypes import render_tvm_stack, deserialize_boc - -from ._wrapper import AsyncTonLibJsonWrapper -from .._utils import b64str_to_hex, hex_to_b64str, hash_to_hex, CtypesStdoutCapture, TonLibWrongResult -from ..._address import prepare_address, detect_address - -logger = logging.getLogger(__name__) - - -class AsyncTonlibClient: - def __init__(self, - config, - keystore, - loop, - cdll_path=None, - verbosity_level=0): - self.ls_index = random.randrange(0, len(config['liteservers'])) - self.config = config - self.keystore = keystore - self.cdll_path = cdll_path - self.loop = loop - self.verbosity_level = verbosity_level - self.max_parallel_requests = config['liteservers'][0].get( - "max_parallel_requests", 50) - - self.semaphore = None - self.tonlib_wrapper = None - self.loaded_contracts_num = None - - @property - def local_config(self): - local = deepcopy(self.config) - local['liteservers'] = [local['liteservers'][self.ls_index]] - return local - - async def reconnect(self, max_restarts=None): - if max_restarts is not None: - max_restarts -= 1 - if max_restarts is None or max_restarts >= 0: - await self.init(max_restarts) - logger.info( - f'Client #{self.ls_index:03d} reconnected (max_restarts: {max_restarts})') - else: - logger.info( - 'Client #{self.ls_index:03d} has no reconnect attempts left') - self.tonlib_wrapper = None - - async def init(self, max_restarts=None): - """ - TL Spec - init options:options = options.Info; - options config:config keystore_type:KeyStoreType = Options; - - keyStoreTypeDirectory directory:string = KeyStoreType; - config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config; - - :param ip: IPv4 address in dotted notation or signed int32 - :param port: IPv4 TCP port - :param key: base64 pub key of liteserver node - :return: None - """ - self.semaphore = asyncio.Semaphore(self.max_parallel_requests) - - self.loaded_contracts_num = 0 - wrapper = AsyncTonLibJsonWrapper(self.loop, self.ls_index, self.cdll_path) - keystore_obj = { - '@type': 'keyStoreTypeDirectory', - 'directory': self.keystore - } - # create keystore - Path(self.keystore).mkdir(parents=True, exist_ok=True) - - request = { - '@type': 'init', - 'options': { - '@type': 'options', - 'config': { - '@type': 'config', - 'config': json.dumps(self.local_config), - 'use_callbacks_for_network': False, - 'blockchain_name': '', - 'ignore_cache': False - }, - 'keystore_type': keystore_obj - } - } - self.tonlib_wrapper = wrapper - with CtypesStdoutCapture(): - # set verbosity level - await self.set_verbosity_level(self.verbosity_level) - - # set confog - init_result = await self.tonlib_wrapper.execute(request) - self.tonlib_wrapper.set_restart_hook( - hook=self.reconnect, max_requests=1024, max_restarts=max_restarts) - - logger.info(F"TonLib #{self.ls_index:03d} inited successfully") - - return init_result - - async def set_verbosity_level(self, level): - request = { - '@type': 'setLogVerbosityLevel', - 'new_verbosity_level': level - } - return await self.tonlib_wrapper.execute(request) - - async def raw_get_transactions(self, account_address: str, from_transaction_lt: str, from_transaction_hash: str): - """ - TL Spec: - raw.getTransactions account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions; - accountAddress account_address:string = AccountAddress; - internal.transactionId lt:int64 hash:bytes = internal.TransactionId; - :param account_address: str with raw or user friendly address - :param from_transaction_lt: from transaction lt - :param from_transaction_hash: from transaction hash in HEX representation - :return: dict as - { - '@type': 'raw.transactions', - 'transactions': list[dict as { - '@type': 'raw.transaction', - 'utime': int, - 'data': str, - 'transaction_id': internal.transactionId, - 'fee': str, - 'in_msg': dict as { - '@type': 'raw.message', - 'source': str, - 'destination': str, - 'value': str, - 'message': str - }, - 'out_msgs': list[dict as raw.message] - }], - 'previous_transaction_id': internal.transactionId - } - """ - account_address = prepare_address(account_address) - from_transaction_hash = hex_to_b64str(from_transaction_hash) - - request = { - '@type': 'raw.getTransactions', - 'account_address': { - 'account_address': account_address, - }, - 'from_transaction_id': { - '@type': 'internal.transactionId', - 'lt': from_transaction_lt, - 'hash': from_transaction_hash - } - } - return await self.tonlib_wrapper.execute(request) - - async def raw_get_account_state(self, address: str): - """ - TL Spec: - raw.getAccountState account_address:accountAddress = raw.AccountState; - accountAddress account_address:string = AccountAddress; - :param address: str with raw or user friendly address - :return: dict as - { - '@type': 'raw.accountState', - 'balance': str, - 'code': str, - 'data': str, - 'last_transaction_id': internal.transactionId, - 'sync_utime': int - } - """ - account_address = prepare_address( - address) # TODO: understand why this is not used - request = { - '@type': 'raw.getAccountState', - 'account_address': { - 'account_address': address - } - } - - return await self.tonlib_wrapper.execute(request) - - async def generic_get_account_state(self, address: str): - # TODO: understand why this is not used - account_address = prepare_address(address) - request = { - '@type': 'getAccountState', - 'account_address': { - 'account_address': address - } - } - return await self.tonlib_wrapper.execute(request) - - async def _load_contract(self, address): - # TODO: understand why this is not used - account_address = prepare_address(address) - request = { - '@type': 'smc.load', - 'account_address': { - 'account_address': address - } - } - result = await self.tonlib_wrapper.execute(request) - if result.get('@type', 'error') == 'error': - raise TonLibWrongResult("smc.load failed", result) - self.loaded_contracts_num += 1 - return result["id"] - - async def raw_run_method(self, address, method, stack_data, output_layout=None): - """ - For numeric data only - TL Spec: - smc.runGetMethod id:int53 method:smc.MethodId stack:vector = smc.RunResult; - - smc.methodIdNumber number:int32 = smc.MethodId; - smc.methodIdName name:string = smc.MethodId; - - tvm.slice bytes:string = tvm.Slice; - tvm.cell bytes:string = tvm.Cell; - tvm.numberDecimal number:string = tvm.Number; - tvm.tuple elements:vector = tvm.Tuple; - tvm.list elements:vector = tvm.List; - - tvm.stackEntrySlice slice:tvm.slice = tvm.StackEntry; - tvm.stackEntryCell cell:tvm.cell = tvm.StackEntry; - tvm.stackEntryNumber number:tvm.Number = tvm.StackEntry; - tvm.stackEntryTuple tuple:tvm.Tuple = tvm.StackEntry; - tvm.stackEntryList list:tvm.List = tvm.StackEntry; - tvm.stackEntryUnsupported = tvm.StackEntry; - - smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; - """ - stack_data = render_tvm_stack(stack_data) - if isinstance(method, int): - method = {'@type': 'smc.methodIdNumber', 'number': method} - else: - method = {'@type': 'smc.methodIdName', 'name': str(method)} - contract_id = await self._load_contract(address) - request = { - '@type': 'smc.runGetMethod', - 'id': contract_id, - 'method': method, - 'stack': stack_data - } - - return await self.tonlib_wrapper.execute(request) - - async def raw_send_message(self, serialized_boc): - """ - raw.sendMessage body:bytes = Ok; - - :param serialized_boc: bytes, serialized bag of cell - """ - serialized_boc = codecs.decode(codecs.encode( - serialized_boc, "base64"), 'utf-8').replace("\n", '') - request = { - '@type': 'raw.sendMessage', - 'body': serialized_boc - } - return await self.tonlib_wrapper.execute(request) - - async def _raw_create_query(self, destination, body, init_code=b'', init_data=b''): - """ - raw.createQuery destination:accountAddress init_code:bytes init_data:bytes body:bytes = query.Info; - - query.info id:int53 valid_until:int53 body_hash:bytes = query.Info; - - """ - init_code = codecs.decode(codecs.encode( - init_code, "base64"), 'utf-8').replace("\n", '') - init_data = codecs.decode(codecs.encode( - init_data, "base64"), 'utf-8').replace("\n", '') - body = codecs.decode(codecs.encode(body, "base64"), - 'utf-8').replace("\n", '') - destination = prepare_address(destination) - request = { - '@type': 'raw.createQuery', - 'body': body, - 'init_code': init_code, - 'init_data': init_data, - 'destination': { - 'account_address': destination - } - } - result = await self.tonlib_wrapper.execute(request) - if result.get('@type', 'error') == 'error': - raise TonLibWrongResult("raw.createQuery failed", result) - return result - - async def _raw_send_query(self, query_info): - """ - query.send id:int53 = Ok; - """ - request = { - '@type': 'query.send', - 'id': query_info['id'] - } - return await self.tonlib_wrapper.execute(request) - - async def raw_create_and_send_query(self, destination, body, init_code=b'', init_data=b''): - query_info = await self._raw_create_query(destination, body, init_code, init_data) - return self._raw_send_query(query_info) - - async def raw_create_and_send_message(self, destination, body, initial_account_state=b''): - # Very close to raw_create_and_send_query, but StateInit should be generated outside - """ - raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok; - - """ - initial_account_state = codecs.decode(codecs.encode( - initial_account_state, "base64"), 'utf-8').replace("\n", '') - body = codecs.decode(codecs.encode(body, "base64"), - 'utf-8').replace("\n", '') - destination = prepare_address(destination) - request = { - '@type': 'raw.createAndSendMessage', - 'destination': { - 'account_address': destination - }, - 'initial_account_state': initial_account_state, - 'data': body - } - return await self.tonlib_wrapper.execute(request) - - async def raw_estimate_fees(self, destination, body, init_code=b'', init_data=b'', ignore_chksig=True): - query_info = await self._raw_create_query(destination, body, init_code, init_data) - request = { - '@type': 'query.estimateFees', - 'id': query_info['id'], - 'ignore_chksig': ignore_chksig - } - return await self.tonlib_wrapper.execute(request) - - async def raw_get_block_transactions(self, fullblock, count, after_tx): - request = { - '@type': 'blocks.getTransactions', - 'id': fullblock, - 'mode': 7 if not after_tx else 7+128, - 'count': count, - 'after': after_tx - } - return await self.tonlib_wrapper.execute(request) - - async def raw_get_block_transactions_ext(self, fullblock, count, after_tx): - request = { - '@type': 'blocks.getTransactionsExt', - 'id': fullblock, - 'mode': 7 if not after_tx else 7+128, - 'count': count, - 'after': after_tx - } - return await self.tonlib_wrapper.execute(request) - - async def get_transactions(self, account, - from_transaction_lt=None, - from_transaction_hash=None, - to_transaction_lt=0, - limit=10, - decode_messages=True, - *args, **kwargs): - """ - Return all transactions between from_transaction_lt and to_transaction_lt - if to_transaction_lt and to_transaction_hash are not defined returns all transactions - if from_transaction_lt and from_transaction_hash are not defined checks last - """ - if from_transaction_hash: - from_transaction_hash = hash_to_hex(from_transaction_hash) - if (from_transaction_lt == None) or (from_transaction_hash == None): - addr = await self.raw_get_account_state(account) - if '@type' in addr and addr['@type'] == "error": - addr = await self.raw_get_account_state(account) - if '@type' in addr and addr['@type'] == "error": - raise TonLibWrongResult("raw.getAccountState failed", addr) - try: - from_transaction_lt, from_transaction_hash = int( - addr["last_transaction_id"]["lt"]), b64str_to_hex(addr["last_transaction_id"]["hash"]) - except KeyError: - raise TonLibWrongResult( - "Can't get last_transaction_id data", addr) - reach_lt = False - all_transactions = [] - current_lt, curret_hash = from_transaction_lt, from_transaction_hash - while (not reach_lt) and (len(all_transactions) < limit): - raw_transactions = await self.raw_get_transactions(account, current_lt, curret_hash) - if(raw_transactions['@type']) == 'error': - break - # TODO probably we should chenge get_transactions API - # if 'message' in raw_transactions['message']: - # raise Exception(raw_transactions['message']) - # else: - # raise Exception("Can't get transactions") - transactions, next = raw_transactions['transactions'], raw_transactions.get( - "previous_transaction_id", None) - for t in transactions: - tlt = int(t['transaction_id']['lt']) - if tlt <= to_transaction_lt: - reach_lt = True - break - all_transactions.append(t) - if next: - current_lt, curret_hash = int( - next["lt"]), b64str_to_hex(next["hash"]) - else: - break - if current_lt == 0: - break - - all_transactions = all_transactions[:limit] - for t in all_transactions: - try: - if "in_msg" in t: - if "source" in t["in_msg"]: - t["in_msg"]["source"] = t["in_msg"]["source"]["account_address"] - if "destination" in t["in_msg"]: - t["in_msg"]["destination"] = t["in_msg"]["destination"]["account_address"] - if decode_messages: - try: - if "msg_data" in t["in_msg"]: - dcd = "" - if t["in_msg"]["msg_data"]["@type"] == "msg.dataRaw": - msg_cell_boc = codecs.decode(codecs.encode( - t["in_msg"]["msg_data"]["body"], 'utf8'), 'base64') - message_cell = deserialize_boc( - msg_cell_boc) - dcd = message_cell.data.data.tobytes() - t["in_msg"]["message"] = codecs.decode( - codecs.encode(dcd, 'base64'), "utf8") - elif t["in_msg"]["msg_data"]["@type"] == "msg.dataText": - dcd = codecs.encode( - t["in_msg"]["msg_data"]["text"], 'utf8') - t["in_msg"]["message"] = codecs.decode( - codecs.decode(dcd, 'base64'), "utf8") - except Exception as e: - t["in_msg"]["message"] = "" - logger.warning( - f"in_msg message decoding exception: {e}") - if "out_msgs" in t: - for o in t["out_msgs"]: - if "source" in o: - o["source"] = o["source"]["account_address"] - if "destination" in o: - o["destination"] = o["destination"]["account_address"] - if decode_messages: - try: - if "msg_data" in o: - dcd = "" - if o["msg_data"]["@type"] == "msg.dataRaw": - msg_cell_boc = codecs.decode(codecs.encode( - o["msg_data"]["body"], 'utf8'), 'base64') - message_cell = deserialize_boc( - msg_cell_boc) - dcd = message_cell.data.data.tobytes() - o["message"] = codecs.decode( - codecs.encode(dcd, 'base64'), "utf8") - elif o["msg_data"]["@type"] == "msg.dataText": - dcd = codecs.encode( - o["msg_data"]["text"], 'utf8') - o["message"] = codecs.decode( - codecs.decode(dcd, 'base64'), "utf8") - except Exception as e: - o["message"] = "" - logger.warning( - f"out_msg message decoding exception: {e}") - except Exception as e: - logger.error(f"getTransaction exception: {e}") - return all_transactions - - async def get_masterchain_info(self, *args, **kwargs): - request = { - '@type': 'blocks.getMasterchainInfo' - } - result = await self.tonlib_wrapper.execute(request) - if result.get('@type', 'error') == 'error': - raise TonLibWrongResult("blocks.getMasterchainInfo failed", result) - return result - - async def lookup_block(self, workchain, shard, seqno=None, lt=None, unixtime=None, *args, **kwargs): - assert seqno or lt or unixtime, "Seqno, LT or unixtime should be defined" - mode = 0 - if seqno: - mode += 1 - if lt: - mode += 2 - if unixtime: - mode += 4 - request = { - '@type': 'blocks.lookupBlock', - 'mode': mode, - 'id': { - '@type': 'ton.blockId', - 'workchain': workchain, - 'shard': shard, - 'seqno': seqno - }, - 'lt': lt, - 'utime': unixtime - } - return await self.tonlib_wrapper.execute(request) - - async def get_shards(self, master_seqno=None, lt=None, unixtime=None, *args, **kwargs): - assert master_seqno or lt or unixtime, "Seqno, LT or unixtime should be defined" - wc, shard = -1, -9223372036854775808 - fullblock = await self.lookup_block(wc, shard, master_seqno, lt, unixtime) - request = { - '@type': 'blocks.getShards', - 'id': fullblock - } - return await self.tonlib_wrapper.execute(request) - - async def get_block_transactions(self, workchain, shard, seqno, count, root_hash=None, file_hash=None, after_lt=None, after_hash=None, *args, **kwargs): - if root_hash and file_hash: - fullblock = { - '@type': 'ton.blockIdExt', - 'workchain': workchain, - 'shard': shard, - 'seqno': seqno, - 'root_hash': root_hash, - 'file_hash': file_hash - } - else: - fullblock = await self.lookup_block(workchain, shard, seqno) - if fullblock.get('@type', 'error') == 'error': - return fullblock - after_tx = { - '@type': 'blocks.accountTransactionId', - 'account': after_hash if after_hash else 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', - 'lt': after_lt if after_lt else 0 - } - total_result = {} - incomplete = True - - while incomplete: - result = await self.raw_get_block_transactions(fullblock, count, after_tx) - if(result['@type']) == 'error': - result = await self.raw_get_block_transactions(fullblock, count, after_tx) - if(result['@type']) == 'error': - raise TonLibWrongResult('Can\'t get blockTransactions', result) - if not total_result: - total_result = result - else: - total_result["transactions"] += result["transactions"] - total_result["incomplete"] = result["incomplete"] - incomplete = result["incomplete"] - if incomplete: - after_tx['account'] = result["transactions"][-1]["account"] - after_tx['lt'] = result["transactions"][-1]["lt"] - - # TODO automatically check incompleteness and download all txes - for tx in total_result["transactions"]: - try: - tx["account"] = "%d:%s" % ( - result["id"]["workchain"], b64str_to_hex(tx["account"])) - except: - pass - return total_result - - async def get_block_transactions_ext(self, - workchain, - shard, - seqno, - count, - root_hash=None, - file_hash=None, - after_lt=None, - after_hash=None, - *args, **kwargs): - if root_hash and file_hash: - fullblock = { - '@type': 'ton.blockIdExt', - 'workchain': workchain, - 'shard': shard, - 'seqno': seqno, - 'root_hash': root_hash, - 'file_hash': file_hash - } - else: - fullblock = await self.lookup_block(workchain, shard, seqno) - if fullblock.get('@type', 'error') == 'error': - return fullblock - after_tx = { - '@type': 'blocks.accountTransactionId', - 'account': after_hash if after_hash else 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', - 'lt': after_lt if after_lt else 0 - } - total_result = {} - incomplete = True - - while incomplete: - result = await self.raw_get_block_transactions_ext(fullblock, count, after_tx) - if(result['@type']) == 'error': - result = await self.raw_get_block_transactions_ext(fullblock, count, after_tx) - if(result['@type']) == 'error': - raise TonLibWrongResult('Can\'t get blockTransactions', result) - if not total_result: - total_result = result - else: - total_result["transactions"] += result["transactions"] - total_result["incomplete"] = result["incomplete"] - incomplete = result["incomplete"] - if incomplete: - account_friendly = result["transactions"][-1]["address"]["account_address"] - hex_without_workchain = detect_address(account_friendly)[ - 'raw_form'].split(':')[1] - after = hex_to_b64str(hex_without_workchain) - after_tx['account'] = after - after_tx['lt'] = result["transactions"][-1]["transaction_id"]["lt"] - - for tx in total_result["transactions"]: - try: - account_friendly = tx["address"]["account_address"] - hex_without_workchain = detect_address(account_friendly)[ - 'raw_form'].split(':')[1] - tx["account"] = "%d:%s" % ( - result["id"]["workchain"], hex_without_workchain) - except: - pass - return total_result - - async def get_block_header(self, workchain, shard, seqno, root_hash=None, file_hash=None, *args, **kwargs): - if root_hash and file_hash: - fullblock = { - '@type': 'ton.blockIdExt', - 'workchain': workchain, - 'shard': shard, - 'seqno': seqno, - 'root_hash': root_hash, - 'file_hash': file_hash - } - else: - fullblock = await self.lookup_block(workchain, shard, seqno) - if fullblock.get('@type', 'error') == 'error': - return fullblock - request = { - '@type': 'blocks.getBlockHeader', - 'id': fullblock - } - return await self.tonlib_wrapper.execute(request) - - async def try_locate_tx_by_incoming_message(self, source, destination, creation_lt, *args, **kwargs): - src = detect_address(source) - dest = detect_address(destination) - workchain = dest["raw_form"].split(":")[0] - shards = await self.get_shards(lt=int(creation_lt)) - - for shard_data in shards['shards']: - shardchain = shard_data['shard'] - for b in range(3): - block = await self.lookup_block(workchain, shardchain, lt=int(creation_lt) + b * 1000000) - txs = await self.get_block_transactions(workchain, - shardchain, - block["seqno"], - count=40, - root_hash=block["root_hash"], - file_hash=block["file_hash"]) - candidate = tuple() - count = 0 - for tx in txs["transactions"]: - if tx["account"] == dest["raw_form"]: - count += 1 - if not candidate or candidate[1] < int(tx["lt"]): - candidate = tx["hash"], int(tx["lt"]) - if candidate: - txses = await self.get_transactions(destination, - from_transaction_lt=candidate[1], - from_transaction_hash=b64str_to_hex( - candidate[0]), - limit=max(count, 10)) - for tx in txses: - try: - in_msg = tx["in_msg"] - tx_source = in_msg["source"] - if len(tx_source) and detect_address(tx_source)["raw_form"] == src["raw_form"]: - if int(in_msg["created_lt"]) == int(creation_lt): - return tx - except KeyError: - pass - raise Exception("Tx not found") - - async def try_locate_tx_by_outcoming_message(self, source, destination, creation_lt, *args, **kwargs): - src = detect_address(source) - dest = detect_address(destination) - workchain = src["raw_form"].split(":")[0] - shards = await self.get_shards(lt=int(creation_lt)) - - for shard_data in shards['shards']: - shardchain = shard_data['shard'] - block = await self.lookup_block(workchain, shardchain, lt=int(creation_lt)) - txses = await self.get_block_transactions(workchain, - shardchain, - block["seqno"], - count=40, - root_hash=block["root_hash"], - file_hash=block["file_hash"]) - candidate = tuple() - count = 0 - for tx in txses["transactions"]: - if tx["account"] == src["raw_form"]: - count += 1 - if not candidate or candidate[1] < int(tx["lt"]): - candidate = tx["hash"], int(tx["lt"]) - if candidate: - txses = await self.get_transactions(source, - from_transaction_lt=candidate[1], - from_transaction_hash=b64str_to_hex( - candidate[0]), - limit=max(count, 10)) - for tx in txses: - try: - for msg in tx["out_msgs"]: - if detect_address(msg["destination"])["raw_form"] == dest["raw_form"]: - if int(msg["created_lt"]) == int(creation_lt): - return tx - except KeyError: - pass - raise Exception("Tx not found") diff --git a/tonsdk/utils/__init__.py b/tonsdk/utils/__init__.py deleted file mode 100644 index 03a0abd..0000000 --- a/tonsdk/utils/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -from ._address import Address -from ._currency import to_nano, from_nano, TonCurrencyEnum -from ._exceptions import InvalidAddressError -from ._utils import concat_bytes, move_to_end, tree_walk, crc32c, \ - crc16, read_n_bytes_uint_from_array, compare_bytes, sign_message, b64str_to_bytes, \ - b64str_to_hex, bytes_to_b64str - -__all__ = [ - 'Address', - 'InvalidAddressError', - - 'concat_bytes', - 'move_to_end', - 'tree_walk', - 'crc32c', - 'crc16', - 'read_n_bytes_uint_from_array', - 'compare_bytes', - 'sign_message', - 'b64str_to_bytes', - 'b64str_to_hex', - 'bytes_to_b64str', - - 'to_nano', - 'from_nano', - 'TonCurrencyEnum', -] diff --git a/tonsdk/__init__.py b/tonsdk_ng/__init__.py similarity index 100% rename from tonsdk/__init__.py rename to tonsdk_ng/__init__.py diff --git a/tonsdk/_exceptions.py b/tonsdk_ng/_exceptions.py similarity index 89% rename from tonsdk/_exceptions.py rename to tonsdk_ng/_exceptions.py index db11b19..a736857 100644 --- a/tonsdk/_exceptions.py +++ b/tonsdk_ng/_exceptions.py @@ -1,10 +1,10 @@ - class TonSdkException(Exception): """ Base class for tonsdk exceptions. Subclasses should provide `.default_detail` properties. """ - default_detail = 'TonSdk error.' + + default_detail = "TonSdk error." def __init__(self, detail=None): self.detail = self.default_detail if detail is None else detail diff --git a/tonsdk/boc/__init__.py b/tonsdk_ng/boc/__init__.py similarity index 57% rename from tonsdk/boc/__init__.py rename to tonsdk_ng/boc/__init__.py index 7bf6bec..1853e12 100644 --- a/tonsdk/boc/__init__.py +++ b/tonsdk_ng/boc/__init__.py @@ -1,12 +1,15 @@ -from ._cell import Cell, deserialize_cell_data, parse_boc_header from ._builder import Builder, begin_cell +from ._cell import Cell, deserialize_cell_data, parse_boc_header from ._dict_builder import DictBuilder, begin_dict from ._slice import Slice __all__ = [ - 'Cell', 'Slice', - 'Builder', 'begin_cell', - 'DictBuilder', 'begin_dict', - 'deserialize_cell_data', - 'parse_boc_header', + "Cell", + "Slice", + "Builder", + "begin_cell", + "DictBuilder", + "begin_dict", + "deserialize_cell_data", + "parse_boc_header", ] diff --git a/tonsdk/boc/_bit_string.py b/tonsdk_ng/boc/_bit_string.py similarity index 95% rename from tonsdk/boc/_bit_string.py rename to tonsdk_ng/boc/_bit_string.py index 39386e5..070e1c7 100644 --- a/tonsdk/boc/_bit_string.py +++ b/tonsdk_ng/boc/_bit_string.py @@ -1,6 +1,6 @@ import copy import math -from typing import Union, Optional +from typing import Optional, Union from ..utils._address import Address @@ -76,7 +76,8 @@ def set_top_upped_array(self, array: bytearray, fullfilled_bytes=True): if not found_end_bit: raise Exception( - f"Incorrect TopUppedArray {array}, {fullfilled_bytes}") + f"Incorrect TopUppedArray {array}, {fullfilled_bytes}" + ) def get_top_upped_array(self) -> bytearray: ret = copy.deepcopy(self) @@ -87,7 +88,7 @@ def get_top_upped_array(self) -> bytearray: while tu > 0: tu -= 1 ret.write_bit(0) - ret.array = ret.array[:math.ceil(ret.cursor / 8)] + ret.array = ret.array[: math.ceil(ret.cursor / 8)] return ret.array def get_free_bits(self) -> int: @@ -99,7 +100,7 @@ def get_used_bits(self): def write_bit_array(self, ba: bytearray): """Writes a bytearray as a bit array one bit by one.""" - for b in ba.decode('utf-8'): + for b in ba.decode("utf-8"): self.write_bit(b) def write_bit(self, b: Union[str, int]): @@ -119,10 +120,11 @@ def write_uint(self, number: int, bit_length: int): return raise Exception( - f"bitLength is too small for number, got number={number},bitLength={bit_length}") + f"bitLength is too small for number, got number={number},bitLength={bit_length}" + ) for i in range(bit_length, 0, -1): - k = (2 ** (i - 1)) + k = 2 ** (i - 1) if number // k == 1: self.write_bit(1) number -= k diff --git a/tonsdk/boc/_builder.py b/tonsdk_ng/boc/_builder.py similarity index 99% rename from tonsdk/boc/_builder.py rename to tonsdk_ng/boc/_builder.py index 3845974..c64db07 100644 --- a/tonsdk/boc/_builder.py +++ b/tonsdk_ng/boc/_builder.py @@ -1,8 +1,7 @@ - - from ._bit_string import BitString from ._cell import Cell + class Builder: def __init__(self): self.bits = BitString(1023) diff --git a/tonsdk/boc/_cell.py b/tonsdk_ng/boc/_cell.py similarity index 76% rename from tonsdk/boc/_cell.py rename to tonsdk_ng/boc/_cell.py index b3add2c..c7f474c 100644 --- a/tonsdk/boc/_cell.py +++ b/tonsdk_ng/boc/_cell.py @@ -2,14 +2,20 @@ import math from hashlib import sha256 +from ..utils import ( + compare_bytes, + concat_bytes, + crc32c, + read_n_bytes_uint_from_array, + tree_walk, +) from ._bit_string import BitString -from ..utils import concat_bytes, tree_walk, crc32c, read_n_bytes_uint_from_array, compare_bytes class Cell: - REACH_BOC_MAGIC_PREFIX = bytes.fromhex('B5EE9C72') - LEAN_BOC_MAGIC_PREFIX = bytes.fromhex('68ff65f3') - LEAN_BOC_MAGIC_PREFIX_CRC = bytes.fromhex('acc3a728') + REACH_BOC_MAGIC_PREFIX = bytes.fromhex("B5EE9C72") + LEAN_BOC_MAGIC_PREFIX = bytes.fromhex("68ff65f3") + LEAN_BOC_MAGIC_PREFIX_CRC = bytes.fromhex("acc3a728") def __init__(self): self.bits = BitString(1023) @@ -52,8 +58,9 @@ def get_data_with_descriptors(self): def get_bits_descriptor(self): d2 = bytearray([0]) - d2[0] = math.ceil(self.bits.cursor / 8) + \ - math.floor(self.bits.cursor / 8) + d2[0] = math.ceil(self.bits.cursor / 8) + math.floor( + self.bits.cursor / 8 + ) return d2 def get_refs_descriptor(self): @@ -63,7 +70,9 @@ def get_refs_descriptor(self): def get_max_level(self): if self.is_exotic: - raise NotImplementedError("Calculating max level for exotic cells is not implemented") + raise NotImplementedError( + "Calculating max level for exotic cells is not implemented" + ) max_level = 0 for r in self.refs: r_max_level = r.get_max_level() @@ -96,18 +105,20 @@ def serialize_for_boc(self, cells_index, ref_size): repr_arr.append(self.get_data_with_descriptors()) if self.is_explicitly_stored_hashes(): - raise NotImplementedError("Cell hashes explicit storing is not implemented") + raise NotImplementedError( + "Cell hashes explicit storing is not implemented" + ) for ref in self.refs: ref_hash = ref.bytes_hash() ref_index_int = cells_index[ref_hash] - ref_index_hex = format(ref_index_int, 'x') + ref_index_hex = format(ref_index_int, "x") if len(ref_index_hex) % 2: - ref_index_hex = '0' + ref_index_hex + ref_index_hex = "0" + ref_index_hex reference = bytes.fromhex(ref_index_hex) repr_arr.append(reference) - x = b'' + x = b"" for data in repr_arr: x = concat_bytes(x, data) @@ -116,7 +127,9 @@ def serialize_for_boc(self, cells_index, ref_size): def boc_serialization_size(self, cells_index, ref_size): return len(self.serialize_for_boc(cells_index, ref_size)) - def to_boc(self, has_idx=True, hash_crc32=True, has_cache_bits=False, flags=0): + def to_boc( + self, has_idx=True, hash_crc32=True, has_cache_bits=False, flags=0 + ): root_cell = copy.deepcopy(self) all_cells = root_cell.tree_walk() @@ -129,18 +142,28 @@ def to_boc(self, has_idx=True, hash_crc32=True, has_cache_bits=False, flags=0): s_bytes = max(math.ceil(s / 8), 1) full_size = 0 cell_sizes = {} - for (_hash, subcell) in topological_order: - cell_sizes[_hash] = subcell.boc_serialization_size(cells_index, s_bytes) + for _hash, subcell in topological_order: + cell_sizes[_hash] = subcell.boc_serialization_size( + cells_index, s_bytes + ) full_size += cell_sizes[_hash] offset_bits = len("{0:b}".format(full_size)) offset_bytes = max(math.ceil(offset_bits / 8), 1) serialization = BitString( - (1023 + 32 * 4 + 32 * 3) * len(topological_order)) + (1023 + 32 * 4 + 32 * 3) * len(topological_order) + ) serialization.write_bytes(Cell.REACH_BOC_MAGIC_PREFIX) - settings = bytes(''.join(['1' if i else '0' for i in [ - has_idx, hash_crc32, has_cache_bits]]), 'utf-8') + settings = bytes( + "".join( + [ + "1" if i else "0" + for i in [has_idx, hash_crc32, has_cache_bits] + ] + ), + "utf-8", + ) serialization.write_bit_array(settings) serialization.write_uint(flags, 2) serialization.write_uint(s_bytes, 3) @@ -152,7 +175,7 @@ def to_boc(self, has_idx=True, hash_crc32=True, has_cache_bits=False, flags=0): serialization.write_uint(0, s_bytes * 8) # Root shoulh have index 0 if has_idx: - for (_hash, subcell) in topological_order: + for _hash, subcell in topological_order: serialization.write_uint(cell_sizes[_hash], offset_bytes * 8) for cell_info in topological_order: @@ -167,6 +190,7 @@ def to_boc(self, has_idx=True, hash_crc32=True, has_cache_bits=False, flags=0): def begin_parse(self): from ._slice import Slice + return Slice(self) @staticmethod @@ -198,17 +222,16 @@ def deserialize_cell_data(cell_data, reference_index_size): raise Exception("Not enough bytes to encode cell data") cell.bits.set_top_upped_array( - bytearray(cell_data[:data_bytes_size]), fullfilled_bytes) + bytearray(cell_data[:data_bytes_size]), fullfilled_bytes + ) cell_data = cell_data[data_bytes_size:] for r in range(ref_num): - cell.refs.append(read_n_bytes_uint_from_array( - reference_index_size, cell_data)) + cell.refs.append( + read_n_bytes_uint_from_array(reference_index_size, cell_data) + ) cell_data = cell_data[reference_index_size:] - return { - "cell": cell, - "residue": cell_data - } + return {"cell": cell, "residue": cell_data} def parse_boc_header(serialized_boc): @@ -247,17 +270,13 @@ def parse_boc_header(serialized_boc): offset_bytes = serialized_boc[0] serialized_boc = serialized_boc[1:] - cells_num = read_n_bytes_uint_from_array( - size_bytes, serialized_boc) + cells_num = read_n_bytes_uint_from_array(size_bytes, serialized_boc) serialized_boc = serialized_boc[size_bytes:] - roots_num = read_n_bytes_uint_from_array( - size_bytes, serialized_boc) + roots_num = read_n_bytes_uint_from_array(size_bytes, serialized_boc) serialized_boc = serialized_boc[size_bytes:] - absent_num = read_n_bytes_uint_from_array( - size_bytes, serialized_boc) + absent_num = read_n_bytes_uint_from_array(size_bytes, serialized_boc) serialized_boc = serialized_boc[size_bytes:] - tot_cells_size = read_n_bytes_uint_from_array( - offset_bytes, serialized_boc) + tot_cells_size = read_n_bytes_uint_from_array(offset_bytes, serialized_boc) serialized_boc = serialized_boc[offset_bytes:] if len(serialized_boc) < roots_num * size_bytes: @@ -265,8 +284,9 @@ def parse_boc_header(serialized_boc): root_list = [] for c in range(roots_num): - root_list.append(read_n_bytes_uint_from_array( - size_bytes, serialized_boc)) + root_list.append( + read_n_bytes_uint_from_array(size_bytes, serialized_boc) + ) serialized_boc = serialized_boc[size_bytes:] index = False @@ -275,8 +295,9 @@ def parse_boc_header(serialized_boc): if len(serialized_boc) < offset_bytes * cells_num: raise Exception("Not enough bytes for index encoding") for c in range(cells_num): - index.append(read_n_bytes_uint_from_array( - offset_bytes, serialized_boc)) + index.append( + read_n_bytes_uint_from_array(offset_bytes, serialized_boc) + ) serialized_boc = serialized_boc[offset_bytes:] if len(serialized_boc) < tot_cells_size: @@ -289,7 +310,9 @@ def parse_boc_header(serialized_boc): raise Exception("Not enough bytes for crc32c hashsum") length = len(input_data) - if not compare_bytes(crc32c(input_data[:length-4]), serialized_boc[:4]): + if not compare_bytes( + crc32c(input_data[: length - 4]), serialized_boc[:4] + ): raise Exception("Crc32c hashsum mismatch") serialized_boc = serialized_boc[4:] @@ -298,19 +321,19 @@ def parse_boc_header(serialized_boc): raise Exception("Too much bytes in BoC serialization") return { - 'has_idx': has_idx, - 'hash_crc32': hash_crc32, - 'has_cache_bits': has_cache_bits, - 'flags': flags, - 'size_bytes': size_bytes, - 'off_bytes': offset_bytes, - 'cells_num': cells_num, - 'roots_num': roots_num, - 'absent_num': absent_num, - 'tot_cells_size': tot_cells_size, - 'root_list': root_list, - 'index': index, - 'cells_data': cells_data, + "has_idx": has_idx, + "hash_crc32": hash_crc32, + "has_cache_bits": has_cache_bits, + "flags": flags, + "size_bytes": size_bytes, + "off_bytes": offset_bytes, + "cells_num": cells_num, + "roots_num": roots_num, + "absent_num": absent_num, + "tot_cells_size": tot_cells_size, + "root_list": root_list, + "index": index, + "cells_data": cells_data, } @@ -319,7 +342,7 @@ def deserialize_boc(serialized_boc): serialized_boc = bytes.fromhex(serialized_boc) header = parse_boc_header(serialized_boc) - cells_data = header['cells_data'] + cells_data = header["cells_data"] cells_array = [] for ci in range(header["cells_num"]): @@ -331,7 +354,7 @@ def deserialize_boc(serialized_boc): c = cells_array[ci] for ri in range(len(c.refs)): r = c.refs[ri] - if (r < ci): + if r < ci: raise Exception("Topological order is broken") c.refs[ri] = cells_array[r] diff --git a/tonsdk/boc/_dict_builder.py b/tonsdk_ng/boc/_dict_builder.py similarity index 71% rename from tonsdk/boc/_dict_builder.py rename to tonsdk_ng/boc/_dict_builder.py index 79826c8..e2303ea 100644 --- a/tonsdk/boc/_dict_builder.py +++ b/tonsdk_ng/boc/_dict_builder.py @@ -1,5 +1,6 @@ -from .dict import serialize_dict from ._cell import Cell +from .dict import serialize_dict + class DictBuilder: def __init__(self, key_size: int): @@ -8,17 +9,17 @@ def __init__(self, key_size: int): self.ended = False def store_cell(self, index, value: Cell): - assert self.ended is False, 'Already ended' + assert self.ended is False, "Already ended" if type(index) == bytes: index = int(index.hex(), 16) - assert type(index) == int, 'Invalid index type' - assert not (index in self.items), f'Item {index} already exist' + assert type(index) == int, "Invalid index type" + assert index not in self.items, f"Item {index} already exist" self.items[index] = value return self def store_ref(self, index, value: Cell): - assert self.ended is False, 'Already ended' + assert self.ended is False, "Already ended" cell = Cell() cell.refs.append(value) @@ -26,7 +27,7 @@ def store_ref(self, index, value: Cell): return self def end_dict(self) -> Cell: - assert self.ended is False, 'Already ended' + assert self.ended is False, "Already ended" self.ended = True if not self.items: return Cell() # ? @@ -37,9 +38,10 @@ def default_serializer(src, dest): return serialize_dict(self.items, self.key_size, default_serializer) def end_cell(self) -> Cell: - assert self.ended is False, 'Already ended' - assert self.items, 'Dict is empty' + assert self.ended is False, "Already ended" + assert self.items, "Dict is empty" return self.end_dict() + def begin_dict(key_size): return DictBuilder(key_size) diff --git a/tonsdk/boc/_slice.py b/tonsdk_ng/boc/_slice.py similarity index 95% rename from tonsdk/boc/_slice.py rename to tonsdk_ng/boc/_slice.py index 9c41617..145015a 100644 --- a/tonsdk/boc/_slice.py +++ b/tonsdk_ng/boc/_slice.py @@ -1,16 +1,18 @@ -import bitarray from typing import Optional -from ._cell import Cell +import bitarray + from ..utils._address import Address +from ._cell import Cell class Slice: """Slice like an analog of slice in FunC. Used only for reading.""" + def __init__(self, cell: Cell): self.bits = bitarray.bitarray() self.bits.frombytes(cell.bits.array) - self.bits = self.bits[:cell.bits.cursor] + self.bits = self.bits[: cell.bits.cursor] self.refs = cell.refs self.ref_offset = 0 @@ -66,13 +68,13 @@ def read_bytes(self, bytes_count: int) -> bytes: def read_int(self, bit_length: int) -> int: if bit_length == 1: # if num is -1 then bit is 1. if 0 then 1. see _bit_string.py - return - self.read_bit() + return -self.read_bit() else: is_negative = self.read_bit() value = self.read_uint(bit_length - 1) if is_negative == 1: # ones complement - return - (2 ** (bit_length - 1) - value) + return -(2 ** (bit_length - 1) - value) else: return value @@ -88,7 +90,7 @@ def read_msg_addr(self) -> Optional[Address]: if self.read_uint(2) == 0: return None self.read_bit() # anycast - workchain_id = hex(self.read_int(8)).replace('0x', '') + workchain_id = hex(self.read_int(8)).replace("0x", "") hashpart = self.read_bytes(32).hex() return Address(workchain_id + ":" + hashpart) diff --git a/tonsdk/boc/dict/__init__.py b/tonsdk_ng/boc/dict/__init__.py similarity index 100% rename from tonsdk/boc/dict/__init__.py rename to tonsdk_ng/boc/dict/__init__.py diff --git a/tonsdk/boc/dict/find_common_prefix.py b/tonsdk_ng/boc/dict/find_common_prefix.py similarity index 94% rename from tonsdk/boc/dict/find_common_prefix.py rename to tonsdk_ng/boc/dict/find_common_prefix.py index b9c90e1..1ada063 100644 --- a/tonsdk/boc/dict/find_common_prefix.py +++ b/tonsdk_ng/boc/dict/find_common_prefix.py @@ -1,8 +1,7 @@ - def find_common_prefix(src): # Corner cases if len(src) == 0: - return '' + return "" if len(src) == 1: return src[0] @@ -16,4 +15,3 @@ def find_common_prefix(src): break return _sorted[0][:size] - diff --git a/tonsdk/boc/dict/serialize_dict.py b/tonsdk_ng/boc/dict/serialize_dict.py similarity index 69% rename from tonsdk/boc/dict/serialize_dict.py rename to tonsdk_ng/boc/dict/serialize_dict.py index a479c63..4a1f9af 100644 --- a/tonsdk/boc/dict/serialize_dict.py +++ b/tonsdk_ng/boc/dict/serialize_dict.py @@ -1,12 +1,12 @@ -from .find_common_prefix import find_common_prefix -from .._cell import Cell - from math import ceil, log2 +from .._cell import Cell +from .find_common_prefix import find_common_prefix + def pad(src: str, size: int) -> str: while len(src) < size: - src = '0' + src + src = "0" + src return src @@ -23,44 +23,39 @@ def remove_prefix_map(src, length): def fork_map(src): - assert len(src) > 0, 'Internal inconsistency' + assert len(src) > 0, "Internal inconsistency" left = {} right = {} for k in src: - if k.find('0') == 0: + if k.find("0") == 0: left[k[1:]] = src[k] else: right[k[1:]] = src[k] - assert len(left) > 0, 'Internal inconsistency. Left empty.' - assert len(right) > 0, 'Internal inconsistency. Left empty.' + assert len(left) > 0, "Internal inconsistency. Left empty." + assert len(right) > 0, "Internal inconsistency. Left empty." return left, right def build_node(src): - assert len(src) > 0, 'Internal inconsistency' + assert len(src) > 0, "Internal inconsistency" if len(src) == 1: - return { - 'type': 'leaf', - 'value': list(src.values())[0] - } + return {"type": "leaf", "value": list(src.values())[0]} left, right = fork_map(src) return { - 'type': 'fork', - 'left': build_edge(left), - 'right': build_edge(right) + "type": "fork", + "left": build_edge(left), + "right": build_edge(right), } def build_edge(src): - assert len(src) > 0, 'Internal inconsistency' + assert len(src) > 0, "Internal inconsistency" label = find_common_prefix(list(src.keys())) return { - 'label': label, - 'node': build_node( - remove_prefix_map(src, len(label)) - ) + "label": label, + "node": build_node(remove_prefix_map(src, len(label))), } @@ -81,12 +76,13 @@ def write_label_short(src, to): to.write_bit(0) # Unary length - for e in src: to.write_bit(1) + for e in src: + to.write_bit(1) to.write_bit(0) # Value for e in src: - to.write_bit(e == '1') + to.write_bit(e == "1") return to @@ -106,7 +102,7 @@ def write_label_long(src, key_length, to): # Value for e in src: - to.write_bit(e == '1') + to.write_bit(e == "1") return to @@ -141,49 +137,49 @@ def is_same(src): def detect_label_type(src, key_size): - kind = 'short' + kind = "short" kind_length = label_short_length(src) long_length = label_long_length(src, key_size) if long_length < kind_length: kind_length = long_length - kind = 'long' + kind = "long" if is_same(src): same_length = label_same_length(key_size) if same_length < kind_length: kind_length = same_length - kind = 'same' + kind = "same" return kind def write_label(src, key_size, to): type = detect_label_type(src, key_size) - if type == 'short': + if type == "short": write_label_short(src, to) - elif type == 'long': + elif type == "long": write_label_long(src, key_size, to) - elif type == 'same': - write_label_same(src[0] == '1', len(src), key_size, to) + elif type == "same": + write_label_same(src[0] == "1", len(src), key_size, to) def write_node(src, key_size, serializer, to): - if src['type'] == 'leaf': - serializer(src['value'], to) + if src["type"] == "leaf": + serializer(src["value"], to) - if src['type'] == 'fork': + if src["type"] == "fork": left_cell = Cell() right_cell = Cell() - write_edge(src['left'], key_size - 1, serializer, left_cell) - write_edge(src['right'], key_size - 1, serializer, right_cell) + write_edge(src["left"], key_size - 1, serializer, left_cell) + write_edge(src["right"], key_size - 1, serializer, right_cell) to.refs.append(left_cell) to.refs.append(right_cell) def write_edge(src, key_size, serializer, to): - write_label(src['label'], key_size, to.bits) - write_node(src['node'], key_size - len(src['label']), serializer, to) + write_label(src["label"], key_size, to.bits) + write_node(src["node"], key_size - len(src["label"]), serializer, to) def serialize_dict(src, key_size, serializer): diff --git a/tonsdk/contract/__init__.py b/tonsdk_ng/contract/__init__.py similarity index 76% rename from tonsdk/contract/__init__.py rename to tonsdk_ng/contract/__init__.py index 4216633..95d4772 100644 --- a/tonsdk/contract/__init__.py +++ b/tonsdk_ng/contract/__init__.py @@ -7,8 +7,9 @@ class Contract(ABC): def __init__(self, **kwargs): self.options = kwargs - self._address = Address( - kwargs["address"]) if "address" in kwargs else None + self._address = ( + Address(kwargs["address"]) if "address" in kwargs else None + ) if "wc" not in kwargs: kwargs["wc"] = self._address.wc if self._address is not None else 0 @@ -25,8 +26,7 @@ def create_state_init(self): state_init = self.__create_state_init(code_cell, data_cell) state_init_hash = state_init.bytes_hash() - address = Address( - str(self.options["wc"]) + ":" + state_init_hash.hex()) + address = Address(str(self.options["wc"]) + ":" + state_init_hash.hex()) return { "code": code_cell, @@ -54,7 +54,6 @@ def create_init_external_message(self): return { "address": address, "message": external_message, - "state_init": state_init, "code": code, "data": data, @@ -70,10 +69,20 @@ def create_external_message_header(cls, dest, src=None, import_fee=0): return message @classmethod - def create_internal_message_header(cls, dest, grams=0, ihr_disabled=True, - bounce=None, bounced=False, src=None, - currency_collection=None, ihr_fees=0, - fwd_fees=0, created_lt=0, created_at=0): + def create_internal_message_header( + cls, + dest, + grams=0, + ihr_disabled=True, + bounce=None, + bounced=False, + src=None, + currency_collection=None, + ihr_fees=0, + fwd_fees=0, + created_lt=0, + created_at=0, + ): message = Cell() message.bits.write_bit(0) message.bits.write_bit(ihr_disabled) @@ -102,7 +111,10 @@ def create_common_msg_info(cls, header, state_init=None, body=None): common_msg_info.write_cell(header) if state_init: common_msg_info.bits.write_bit(1) - if common_msg_info.bits.get_free_bits() - 1 >= state_init.bits.get_used_bits(): + if ( + common_msg_info.bits.get_free_bits() - 1 + >= state_init.bits.get_used_bits() + ): common_msg_info.bits.write_bit(0) common_msg_info.write_cell(state_init) else: @@ -112,7 +124,10 @@ def create_common_msg_info(cls, header, state_init=None, body=None): common_msg_info.bits.write_bit(0) if body: - if common_msg_info.bits.get_free_bits() >= body.bits.get_used_bits(): + if ( + common_msg_info.bits.get_free_bits() + >= body.bits.get_used_bits() + ): common_msg_info.bits.write_bit(0) common_msg_info.write_cell(body) else: @@ -123,14 +138,30 @@ def create_common_msg_info(cls, header, state_init=None, body=None): return common_msg_info - def __create_state_init(self, code, data, library=None, split_depth=None, ticktock=None): + def __create_state_init( + self, code, data, library=None, split_depth=None, ticktock=None + ): if library or split_depth or ticktock: raise Exception( - "Library/SplitDepth/Ticktock in state init is not implemented") + "Library/SplitDepth/Ticktock in state init is not implemented" + ) state_init = Cell() - settings = bytes(''.join(['1' if i else '0' for i in [bool(split_depth), bool( - ticktock), bool(code), bool(data), bool(library)]]), 'utf-8') + settings = bytes( + "".join( + [ + "1" if i else "0" + for i in [ + bool(split_depth), + bool(ticktock), + bool(code), + bool(data), + bool(library), + ] + ] + ), + "utf-8", + ) state_init.bits.write_bit_array(settings) if code: diff --git a/tonsdk/contract/token/__init__.py b/tonsdk_ng/contract/token/__init__.py similarity index 100% rename from tonsdk/contract/token/__init__.py rename to tonsdk_ng/contract/token/__init__.py diff --git a/tonsdk/contract/token/ft/__init__.py b/tonsdk_ng/contract/token/ft/__init__.py similarity index 68% rename from tonsdk/contract/token/ft/__init__.py rename to tonsdk_ng/contract/token/ft/__init__.py index b1c2062..d7d8e53 100644 --- a/tonsdk/contract/token/ft/__init__.py +++ b/tonsdk_ng/contract/token/ft/__init__.py @@ -2,6 +2,6 @@ from .jetton_wallet import JettonWallet __all__ = [ - 'JettonMinter', - 'JettonWallet', -] \ No newline at end of file + "JettonMinter", + "JettonWallet", +] diff --git a/tonsdk/contract/token/ft/jetton_minter.py b/tonsdk_ng/contract/token/ft/jetton_minter.py similarity index 69% rename from tonsdk/contract/token/ft/jetton_minter.py rename to tonsdk_ng/contract/token/ft/jetton_minter.py index d9e3efe..d2d7b5c 100644 --- a/tonsdk/contract/token/ft/jetton_minter.py +++ b/tonsdk_ng/contract/token/ft/jetton_minter.py @@ -1,28 +1,36 @@ - - -from ..nft.nft_utils import create_offchain_uri_cell -from ... import Contract from ....boc import Cell from ....utils import Address +from ... import Contract +from ..nft.nft_utils import create_offchain_uri_cell class JettonMinter(Contract): - code = 'B5EE9C7241020B010001ED000114FF00F4A413F4BCF2C80B0102016202030202CC040502037A60090A03EFD9910E38048ADF068698180B8D848ADF07D201800E98FE99FF6A2687D007D206A6A18400AA9385D47181A9AA8AAE382F9702480FD207D006A18106840306B90FD001812881A28217804502A906428027D012C678B666664F6AA7041083DEECBEF29385D71811A92E001F1811802600271812F82C207F97840607080093DFC142201B82A1009AA0A01E428027D012C678B00E78B666491646580897A007A00658064907C80383A6465816503E5FFE4E83BC00C646582AC678B28027D0109E5B589666664B8FD80400FE3603FA00FA40F82854120870542013541403C85004FA0258CF1601CF16CCC922C8CB0112F400F400CB00C9F9007074C8CB02CA07CBFFC9D05008C705F2E04A12A1035024C85004FA0258CF16CCCCC9ED5401FA403020D70B01C3008E1F8210D53276DB708010C8CB055003CF1622FA0212CB6ACB1FCB3FC98042FB00915BE200303515C705F2E049FA403059C85004FA0258CF16CCCCC9ED54002E5143C705F2E049D43001C85004FA0258CF16CCCCC9ED54007DADBCF6A2687D007D206A6A183618FC1400B82A1009AA0A01E428027D012C678B00E78B666491646580897A007A00658064FC80383A6465816503E5FFE4E840001FAF16F6A2687D007D206A6A183FAA904051007F09' + code = "B5EE9C7241020B010001ED000114FF00F4A413F4BCF2C80B0102016202030202CC040502037A60090A03EFD9910E38048ADF068698180B8D848ADF07D201800E98FE99FF6A2687D007D206A6A18400AA9385D47181A9AA8AAE382F9702480FD207D006A18106840306B90FD001812881A28217804502A906428027D012C678B666664F6AA7041083DEECBEF29385D71811A92E001F1811802600271812F82C207F97840607080093DFC142201B82A1009AA0A01E428027D012C678B00E78B666491646580897A007A00658064907C80383A6465816503E5FFE4E83BC00C646582AC678B28027D0109E5B589666664B8FD80400FE3603FA00FA40F82854120870542013541403C85004FA0258CF1601CF16CCC922C8CB0112F400F400CB00C9F9007074C8CB02CA07CBFFC9D05008C705F2E04A12A1035024C85004FA0258CF16CCCCC9ED5401FA403020D70B01C3008E1F8210D53276DB708010C8CB055003CF1622FA0212CB6ACB1FCB3FC98042FB00915BE200303515C705F2E049FA403059C85004FA0258CF16CCCCC9ED54002E5143C705F2E049D43001C85004FA0258CF16CCCCC9ED54007DADBCF6A2687D007D206A6A183618FC1400B82A1009AA0A01E428027D012C678B00E78B666491646580897A007A00658064FC80383A6465816503E5FFE4E840001FAF16F6A2687D007D206A6A183FAA904051007F09" def __init__(self, **kwargs): - self.code = kwargs.get('code') or self.code - kwargs['code'] = Cell.one_from_boc(self.code) + self.code = kwargs.get("code") or self.code + kwargs["code"] = Cell.one_from_boc(self.code) super().__init__(**kwargs) def create_data_cell(self) -> Cell: cell = Cell() cell.bits.write_grams(0) # total supply - cell.bits.write_address(self.options['admin_address']) - cell.refs.append(create_offchain_uri_cell(self.options['jetton_content_uri'])) - cell.refs.append(Cell.one_from_boc(self.options['jetton_wallet_code_hex'])) + cell.bits.write_address(self.options["admin_address"]) + cell.refs.append( + create_offchain_uri_cell(self.options["jetton_content_uri"]) + ) + cell.refs.append( + Cell.one_from_boc(self.options["jetton_wallet_code_hex"]) + ) return cell - def create_mint_body(self, destination: Address, jetton_amount: int, amount: int = 50000000, query_id: int = 0) -> Cell: + def create_mint_body( + self, + destination: Address, + jetton_amount: int, + amount: int = 50000000, + query_id: int = 0, + ) -> Cell: body = Cell() body.bits.write_uint(21, 32) # OP mint body.bits.write_uint(query_id, 64) @@ -30,28 +38,33 @@ def create_mint_body(self, destination: Address, jetton_amount: int, amount: int body.bits.write_grams(amount) transfer_body = Cell() # internal transfer - transfer_body.bits.write_uint(0x178d4519, 32) # OP transfer + transfer_body.bits.write_uint(0x178D4519, 32) # OP transfer transfer_body.bits.write_uint(query_id, 64) transfer_body.bits.write_grams(jetton_amount) # jetton amount transfer_body.bits.write_address(None) # from_address transfer_body.bits.write_address(None) # response_address transfer_body.bits.write_grams(0) # forward amount - transfer_body.bits.write_bit(0) # forward_payload in this slice, not separate cell + transfer_body.bits.write_bit( + 0 + ) # forward_payload in this slice, not separate cell body.refs.append(transfer_body) return body - def create_change_admin_body(self, new_admin_address: Address, query_id: int = 0) -> Cell: + def create_change_admin_body( + self, new_admin_address: Address, query_id: int = 0 + ) -> Cell: body = Cell() body.bits.write_uint(3, 32) # OP body.bits.write_uint(query_id, 64) # query_id body.bits.write_address(new_admin_address) return body - def create_edit_content_body(self, jetton_content_uri: str, query_id: int = 0) -> Cell: + def create_edit_content_body( + self, jetton_content_uri: str, query_id: int = 0 + ) -> Cell: body = Cell() body.bits.write_uint(4, 32) # OP body.bits.write_uint(query_id, 64) # query_id body.refs.append(create_offchain_uri_cell(jetton_content_uri)) return body - diff --git a/tonsdk/contract/token/ft/jetton_wallet.py b/tonsdk_ng/contract/token/ft/jetton_wallet.py similarity index 75% rename from tonsdk/contract/token/ft/jetton_wallet.py rename to tonsdk_ng/contract/token/ft/jetton_wallet.py index 02733ad..e989611 100644 --- a/tonsdk/contract/token/ft/jetton_wallet.py +++ b/tonsdk_ng/contract/token/ft/jetton_wallet.py @@ -1,44 +1,49 @@ - - -from ... import Contract from ....boc import Cell from ....utils import Address +from ... import Contract class JettonWallet(Contract): - code = 'B5EE9C7241021201000328000114FF00F4A413F4BCF2C80B0102016202030202CC0405001BA0F605DA89A1F401F481F481A8610201D40607020148080900BB0831C02497C138007434C0C05C6C2544D7C0FC02F83E903E900C7E800C5C75C87E800C7E800C00B4C7E08403E29FA954882EA54C4D167C0238208405E3514654882EA58C511100FC02780D60841657C1EF2EA4D67C02B817C12103FCBC2000113E910C1C2EBCB853600201200A0B020120101101F500F4CFFE803E90087C007B51343E803E903E90350C144DA8548AB1C17CB8B04A30BFFCB8B0950D109C150804D50500F214013E809633C58073C5B33248B232C044BD003D0032C032483E401C1D3232C0B281F2FFF274013E903D010C7E801DE0063232C1540233C59C3E8085F2DAC4F3208405E351467232C7C6600C03F73B51343E803E903E90350C0234CFFE80145468017E903E9014D6F1C1551CDB5C150804D50500F214013E809633C58073C5B33248B232C044BD003D0032C0327E401C1D3232C0B281F2FFF274140371C1472C7CB8B0C2BE80146A2860822625A020822625A004AD822860822625A028062849F8C3C975C2C070C008E00D0E0F009ACB3F5007FA0222CF165006CF1625FA025003CF16C95005CC2391729171E25008A813A08208989680AA008208989680A0A014BCF2E2C504C98040FB001023C85004FA0258CF1601CF16CCC9ED5400705279A018A182107362D09CC8CB1F5230CB3F58FA025007CF165007CF16C9718018C8CB0524CF165006FA0215CB6A14CCC971FB0010241023000E10491038375F040076C200B08E218210D53276DB708010C8CB055008CF165004FA0216CB6A12CB1F12CB3FC972FB0093356C21E203C85004FA0258CF1601CF16CCC9ED5400DB3B51343E803E903E90350C01F4CFFE803E900C145468549271C17CB8B049F0BFFCB8B0A0822625A02A8005A805AF3CB8B0E0841EF765F7B232C7C572CFD400FE8088B3C58073C5B25C60063232C14933C59C3E80B2DAB33260103EC01004F214013E809633C58073C5B3327B55200083200835C87B51343E803E903E90350C0134C7E08405E3514654882EA0841EF765F784EE84AC7CB8B174CFCC7E800C04E81408F214013E809633C58073C5B3327B55205ECCF23D' + code = "B5EE9C7241021201000328000114FF00F4A413F4BCF2C80B0102016202030202CC0405001BA0F605DA89A1F401F481F481A8610201D40607020148080900BB0831C02497C138007434C0C05C6C2544D7C0FC02F83E903E900C7E800C5C75C87E800C7E800C00B4C7E08403E29FA954882EA54C4D167C0238208405E3514654882EA58C511100FC02780D60841657C1EF2EA4D67C02B817C12103FCBC2000113E910C1C2EBCB853600201200A0B020120101101F500F4CFFE803E90087C007B51343E803E903E90350C144DA8548AB1C17CB8B04A30BFFCB8B0950D109C150804D50500F214013E809633C58073C5B33248B232C044BD003D0032C032483E401C1D3232C0B281F2FFF274013E903D010C7E801DE0063232C1540233C59C3E8085F2DAC4F3208405E351467232C7C6600C03F73B51343E803E903E90350C0234CFFE80145468017E903E9014D6F1C1551CDB5C150804D50500F214013E809633C58073C5B33248B232C044BD003D0032C0327E401C1D3232C0B281F2FFF274140371C1472C7CB8B0C2BE80146A2860822625A020822625A004AD822860822625A028062849F8C3C975C2C070C008E00D0E0F009ACB3F5007FA0222CF165006CF1625FA025003CF16C95005CC2391729171E25008A813A08208989680AA008208989680A0A014BCF2E2C504C98040FB001023C85004FA0258CF1601CF16CCC9ED5400705279A018A182107362D09CC8CB1F5230CB3F58FA025007CF165007CF16C9718018C8CB0524CF165006FA0215CB6A14CCC971FB0010241023000E10491038375F040076C200B08E218210D53276DB708010C8CB055008CF165004FA0216CB6A12CB1F12CB3FC972FB0093356C21E203C85004FA0258CF1601CF16CCC9ED5400DB3B51343E803E903E90350C01F4CFFE803E900C145468549271C17CB8B049F0BFFCB8B0A0822625A02A8005A805AF3CB8B0E0841EF765F7B232C7C572CFD400FE8088B3C58073C5B25C60063232C14933C59C3E80B2DAB33260103EC01004F214013E809633C58073C5B3327B55200083200835C87B51343E803E903E90350C0134C7E08405E3514654882EA0841EF765F784EE84AC7CB8B174CFCC7E800C04E81408F214013E809633C58073C5B3327B55205ECCF23D" def __init__(self, **kwargs): - self.code = kwargs.get('code') or self.code + self.code = kwargs.get("code") or self.code kwargs["code"] = Cell.one_from_boc(self.code) super().__init__(**kwargs) def create_transfer_body( - self, - to_address: Address, - jetton_amount: int, - forward_amount: int = 0, - forward_payload: bytes = None, - response_address: Address = None, - query_id: int = 0 + self, + to_address: Address, + jetton_amount: int, + forward_amount: int = 0, + forward_payload: bytes = None, + response_address: Address = None, + query_id: int = 0, ) -> Cell: cell = Cell() - cell.bits.write_uint(0xf8a7ea5, 32) # request_transfer op + cell.bits.write_uint(0xF8A7EA5, 32) # request_transfer op cell.bits.write_uint(query_id, 64) cell.bits.write_grams(jetton_amount) cell.bits.write_address(to_address) cell.bits.write_address(response_address or to_address) cell.bits.write_bit(0) # null custom_payload cell.bits.write_grams(forward_amount) - cell.bits.write_bit(0) # forward_payload in this slice, not separate cell + cell.bits.write_bit( + 0 + ) # forward_payload in this slice, not separate cell if forward_payload: cell.bits.write_bytes(forward_payload) return cell - def create_burn_body(self, jetton_amount: int, response_address: Address = None, query_id: int = 0) -> Cell: + def create_burn_body( + self, + jetton_amount: int, + response_address: Address = None, + query_id: int = 0, + ) -> Cell: cell = Cell() - cell.bits.write_uint(0x595f07bc, 32) # burn OP + cell.bits.write_uint(0x595F07BC, 32) # burn OP cell.bits.write_uint(query_id, 64) cell.bits.write_grams(jetton_amount) cell.bits.write_address(response_address) diff --git a/tonsdk/contract/token/nft/__init__.py b/tonsdk_ng/contract/token/nft/__init__.py similarity index 68% rename from tonsdk/contract/token/nft/__init__.py rename to tonsdk_ng/contract/token/nft/__init__.py index cd1584b..41b235d 100644 --- a/tonsdk/contract/token/nft/__init__.py +++ b/tonsdk_ng/contract/token/nft/__init__.py @@ -3,7 +3,7 @@ from .nft_sale import NFTSale __all__ = [ - 'NFTItem', - 'NFTCollection', - 'NFTSale', -] \ No newline at end of file + "NFTItem", + "NFTCollection", + "NFTSale", +] diff --git a/tonsdk/contract/token/nft/nft_collection.py b/tonsdk_ng/contract/token/nft/nft_collection.py similarity index 69% rename from tonsdk/contract/token/nft/nft_collection.py rename to tonsdk_ng/contract/token/nft/nft_collection.py index 8317b17..efdb560 100644 --- a/tonsdk/contract/token/nft/nft_collection.py +++ b/tonsdk_ng/contract/token/nft/nft_collection.py @@ -1,53 +1,60 @@ from math import floor from typing import List, Tuple -from .nft_utils import create_offchain_uri_cell, serialize_uri -from ... import Contract from ....boc import Cell, DictBuilder from ....utils import Address +from ... import Contract +from .nft_utils import create_offchain_uri_cell, serialize_uri class NFTCollection(Contract): - code = 'B5EE9C724102140100021F000114FF00F4A413F4BCF2C80B0102016202030202CD04050201200E0F04E7D10638048ADF000E8698180B8D848ADF07D201800E98FE99FF6A2687D20699FEA6A6A184108349E9CA829405D47141BAF8280E8410854658056B84008646582A802E78B127D010A65B509E58FE59F80E78B64C0207D80701B28B9E382F970C892E000F18112E001718112E001F181181981E0024060708090201200A0B00603502D33F5313BBF2E1925313BA01FA00D43028103459F0068E1201A44343C85005CF1613CB3FCCCCCCC9ED54925F05E200A6357003D4308E378040F4966FA5208E2906A4208100FABE93F2C18FDE81019321A05325BBF2F402FA00D43022544B30F00623BA9302A402DE04926C21E2B3E6303250444313C85005CF1613CB3FCCCCCCC9ED54002C323401FA40304144C85005CF1613CB3FCCCCCCC9ED54003C8E15D4D43010344130C85005CF1613CB3FCCCCCCC9ED54E05F04840FF2F00201200C0D003D45AF0047021F005778018C8CB0558CF165004FA0213CB6B12CCCCC971FB008002D007232CFFE0A33C5B25C083232C044FD003D0032C03260001B3E401D3232C084B281F2FFF2742002012010110025BC82DF6A2687D20699FEA6A6A182DE86A182C40043B8B5D31ED44D0FA40D33FD4D4D43010245F04D0D431D430D071C8CB0701CF16CCC980201201213002FB5DAFDA89A1F481A67FA9A9A860D883A1A61FA61FF480610002DB4F47DA89A1F481A67FA9A9A86028BE09E008E003E00B01A500C6E' + code = "B5EE9C724102140100021F000114FF00F4A413F4BCF2C80B0102016202030202CD04050201200E0F04E7D10638048ADF000E8698180B8D848ADF07D201800E98FE99FF6A2687D20699FEA6A6A184108349E9CA829405D47141BAF8280E8410854658056B84008646582A802E78B127D010A65B509E58FE59F80E78B64C0207D80701B28B9E382F970C892E000F18112E001718112E001F181181981E0024060708090201200A0B00603502D33F5313BBF2E1925313BA01FA00D43028103459F0068E1201A44343C85005CF1613CB3FCCCCCCC9ED54925F05E200A6357003D4308E378040F4966FA5208E2906A4208100FABE93F2C18FDE81019321A05325BBF2F402FA00D43022544B30F00623BA9302A402DE04926C21E2B3E6303250444313C85005CF1613CB3FCCCCCCC9ED54002C323401FA40304144C85005CF1613CB3FCCCCCCC9ED54003C8E15D4D43010344130C85005CF1613CB3FCCCCCCC9ED54E05F04840FF2F00201200C0D003D45AF0047021F005778018C8CB0558CF165004FA0213CB6B12CCCCC971FB008002D007232CFFE0A33C5B25C083232C044FD003D0032C03260001B3E401D3232C084B281F2FFF2742002012010110025BC82DF6A2687D20699FEA6A6A182DE86A182C40043B8B5D31ED44D0FA40D33FD4D4D43010245F04D0D431D430D071C8CB0701CF16CCC980201201213002FB5DAFDA89A1F481A67FA9A9A860D883A1A61FA61FF480610002DB4F47DA89A1F481A67FA9A9A86028BE09E008E003E00B01A500C6E" def __init__(self, **kwargs): - self.code = kwargs.get('code') or self.code + self.code = kwargs.get("code") or self.code kwargs["code"] = Cell.one_from_boc(self.code) super().__init__(**kwargs) - self.options['royalty_base'] = self.options.get('royalty_base', 1000) - self.options['royalty_factor'] = floor(self.options.get('royalty', 0) * self.options['royalty_base']) - + self.options["royalty_base"] = self.options.get("royalty_base", 1000) + self.options["royalty_factor"] = floor( + self.options.get("royalty", 0) * self.options["royalty_base"] + ) def create_content_cell(self, params) -> Cell: - collection_content_cell = create_offchain_uri_cell(params['collection_content_uri']) + collection_content_cell = create_offchain_uri_cell( + params["collection_content_uri"] + ) common_content_cell = Cell() - common_content_cell.bits.write_bytes(serialize_uri(params['nft_item_content_base_uri'])) + common_content_cell.bits.write_bytes( + serialize_uri(params["nft_item_content_base_uri"]) + ) content_cell = Cell() content_cell.refs.append(collection_content_cell) content_cell.refs.append(common_content_cell) return content_cell - def create_royalty_cell(self, params) -> Cell: royalty_cell = Cell() - royalty_cell.bits.write_uint(params['royalty_factor'], 16) - royalty_cell.bits.write_uint(params['royalty_base'], 16) - royalty_cell.bits.write_address(params['royalty_address']) + royalty_cell.bits.write_uint(params["royalty_factor"], 16) + royalty_cell.bits.write_uint(params["royalty_base"], 16) + royalty_cell.bits.write_address(params["royalty_address"]) return royalty_cell - def create_data_cell(self) -> Cell: cell = Cell() - cell.bits.write_address(self.options['owner_address']) + cell.bits.write_address(self.options["owner_address"]) cell.bits.write_uint(0, 64) # next_item_index cell.refs.append(self.create_content_cell(self.options)) - cell.refs.append(Cell.one_from_boc(self.options['nft_item_code_hex'])) + cell.refs.append(Cell.one_from_boc(self.options["nft_item_code_hex"])) cell.refs.append(self.create_royalty_cell(self.options)) return cell def create_mint_body( - self, item_index: int, new_owner_address: Address, - item_content_uri: str, amount: int = 50000000, query_id: int = 0 + self, + item_index: int, + new_owner_address: Address, + item_content_uri: str, + amount: int = 50000000, + query_id: int = 0, ) -> Cell: body = Cell() body.bits.write_uint(1, 32) @@ -61,18 +68,21 @@ def create_mint_body( content_cell.refs.append(uri_content) body.refs.append(content_cell) return body - + def create_batch_mint_body( - self, from_item_index: int, - contents_and_owners: List[Tuple[str, Address]], - amount_per_one: int = 50000000, query_id: int = 0 + self, + from_item_index: int, + contents_and_owners: List[Tuple[str, Address]], + amount_per_one: int = 50000000, + query_id: int = 0, ) -> Cell: body = Cell() body.bits.write_uint(2, 32) body.bits.write_uint(query_id, 64) deploy_list = DictBuilder(64) - for i, (item_content_uri, new_owner_address) in \ - enumerate(contents_and_owners): + for i, (item_content_uri, new_owner_address) in enumerate( + contents_and_owners + ): item = Cell() item.bits.write_grams(amount_per_one) content = Cell() @@ -87,11 +97,13 @@ def create_batch_mint_body( def create_get_royalty_params_body(self, query_id: int = 0) -> Cell: body = Cell() - body.bits.write_uint(0x693d3950, 32) # OP + body.bits.write_uint(0x693D3950, 32) # OP body.bits.write_uint(query_id, 64) # query_id return body - def create_change_owner_body(self, new_owner_address: Address, query_id: int = 0) -> Cell: + def create_change_owner_body( + self, new_owner_address: Address, query_id: int = 0 + ) -> Cell: body = Cell() body.bits.write_uint(3, 32) # OP body.bits.write_uint(query_id, 64) # query_id @@ -99,14 +111,12 @@ def create_change_owner_body(self, new_owner_address: Address, query_id: int = 0 return body def create_edit_content_body(self, params) -> Cell: - if params['royalty'] > 1: - raise Exception('royalty must be less than 1') + if params["royalty"] > 1: + raise Exception("royalty must be less than 1") body = Cell() body.bits.write_uint(4, 32) # OP - body.bits.write_uint(params.get('query_id', 0), 64) # query_id + body.bits.write_uint(params.get("query_id", 0), 64) # query_id body.refs.append(self.create_content_cell(params)) body.refs.append(self.create_royalty_cell(params)) return body - - diff --git a/tonsdk/contract/token/nft/nft_item.py b/tonsdk_ng/contract/token/nft/nft_item.py similarity index 67% rename from tonsdk/contract/token/nft/nft_item.py rename to tonsdk_ng/contract/token/nft/nft_item.py index 22c761a..44dde65 100644 --- a/tonsdk/contract/token/nft/nft_item.py +++ b/tonsdk_ng/contract/token/nft/nft_item.py @@ -1,38 +1,44 @@ -from ... import Contract from ....boc import Cell from ....utils import Address +from ... import Contract class NFTItem(Contract): - code = 'B5EE9C7241020D010001D0000114FF00F4A413F4BCF2C80B0102016202030202CE04050009A11F9FE00502012006070201200B0C02D70C8871C02497C0F83434C0C05C6C2497C0F83E903E900C7E800C5C75C87E800C7E800C3C00812CE3850C1B088D148CB1C17CB865407E90350C0408FC00F801B4C7F4CFE08417F30F45148C2EA3A1CC840DD78C9004F80C0D0D0D4D60840BF2C9A884AEB8C097C12103FCBC20080900113E910C1C2EBCB8536001F65135C705F2E191FA4021F001FA40D20031FA00820AFAF0801BA121945315A0A1DE22D70B01C300209206A19136E220C2FFF2E192218E3E821005138D91C85009CF16500BCF16712449145446A0708010C8CB055007CF165005FA0215CB6A12CB1FCB3F226EB39458CF17019132E201C901FB00104794102A375BE20A00727082108B77173505C8CBFF5004CF1610248040708010C8CB055007CF165005FA0215CB6A12CB1FCB3F226EB39458CF17019132E201C901FB000082028E3526F0018210D53276DB103744006D71708010C8CB055007CF165005FA0215CB6A12CB1FCB3F226EB39458CF17019132E201C901FB0093303234E25502F003003B3B513434CFFE900835D27080269FC07E90350C04090408F80C1C165B5B60001D00F232CFD633C58073C5B3327B5520BF75041B' + code = "B5EE9C7241020D010001D0000114FF00F4A413F4BCF2C80B0102016202030202CE04050009A11F9FE00502012006070201200B0C02D70C8871C02497C0F83434C0C05C6C2497C0F83E903E900C7E800C5C75C87E800C7E800C3C00812CE3850C1B088D148CB1C17CB865407E90350C0408FC00F801B4C7F4CFE08417F30F45148C2EA3A1CC840DD78C9004F80C0D0D0D4D60840BF2C9A884AEB8C097C12103FCBC20080900113E910C1C2EBCB8536001F65135C705F2E191FA4021F001FA40D20031FA00820AFAF0801BA121945315A0A1DE22D70B01C300209206A19136E220C2FFF2E192218E3E821005138D91C85009CF16500BCF16712449145446A0708010C8CB055007CF165005FA0215CB6A12CB1FCB3F226EB39458CF17019132E201C901FB00104794102A375BE20A00727082108B77173505C8CBFF5004CF1610248040708010C8CB055007CF165005FA0215CB6A12CB1FCB3F226EB39458CF17019132E201C901FB000082028E3526F0018210D53276DB103744006D71708010C8CB055007CF165005FA0215CB6A12CB1FCB3F226EB39458CF17019132E201C901FB0093303234E25502F003003B3B513434CFFE900835D27080269FC07E90350C04090408F80C1C165B5B60001D00F232CFD633C58073C5B3327B5520BF75041B" def __init__(self, **kwargs): - self.code = kwargs.get('code') or self.code + self.code = kwargs.get("code") or self.code kwargs["code"] = Cell.one_from_boc(self.code) super().__init__(**kwargs) def create_data_cell(self) -> Cell: cell = Cell() - cell.bits.write_uint(self.options.get('index', 0), 64) - cell.bits.write_address(self.options.get('collection_address', None)) - if 'owner_address' in self.options: - cell.bits.write_address(self.options['owner_address']) - if 'content' in self.options: - cell.refs.append(self.options['content']) + cell.bits.write_uint(self.options.get("index", 0), 64) + cell.bits.write_address(self.options.get("collection_address", None)) + if "owner_address" in self.options: + cell.bits.write_address(self.options["owner_address"]) + if "content" in self.options: + cell.refs.append(self.options["content"]) return cell def create_transfer_body( - self, new_owner_address: Address, response_address: Address = None, - forward_amount: int = 0, forward_payload: bytes = None, query_id: int = 0 + self, + new_owner_address: Address, + response_address: Address = None, + forward_amount: int = 0, + forward_payload: bytes = None, + query_id: int = 0, ) -> Cell: cell = Cell() - cell.bits.write_uint(0x5fcc3d14, 32) # transfer OP + cell.bits.write_uint(0x5FCC3D14, 32) # transfer OP cell.bits.write_uint(query_id, 64) cell.bits.write_address(new_owner_address) cell.bits.write_address(response_address or new_owner_address) cell.bits.write_bit(False) # null custom_payload cell.bits.write_grams(forward_amount) - cell.bits.write_bit(False) # forward_payload in this slice, not separate cell + cell.bits.write_bit( + False + ) # forward_payload in this slice, not separate cell if forward_payload: cell.bits.write_bytes(forward_payload) @@ -40,9 +46,6 @@ def create_transfer_body( def create_get_static_data_body(self, query_id: int = 0) -> Cell: cell = Cell() - cell.bits.write_uint(0x2fcb26a2, 32) + cell.bits.write_uint(0x2FCB26A2, 32) cell.bits.write_uint(query_id, 64) return cell - - - diff --git a/tonsdk/contract/token/nft/nft_sale.py b/tonsdk_ng/contract/token/nft/nft_sale.py similarity index 73% rename from tonsdk/contract/token/nft/nft_sale.py rename to tonsdk_ng/contract/token/nft/nft_sale.py index 48b46c0..0baf032 100644 --- a/tonsdk/contract/token/nft/nft_sale.py +++ b/tonsdk_ng/contract/token/nft/nft_sale.py @@ -1,28 +1,26 @@ - - -from ... import Contract from ....boc import Cell +from ... import Contract class NFTSale(Contract): - code = 'B5EE9C7241020A010001B4000114FF00F4A413F4BCF2C80B01020120020302014804050004F2300202CD0607002FA03859DA89A1F481F481F481F401A861A1F401F481F4006101F7D00E8698180B8D8492F82707D201876A2687D207D207D207D006A18116BA4E10159C71D991B1B2990E382C92F837028916382F970FA01698FC1080289C6C8895D7970FAE99F98FD2018201A642802E78B2801E78B00E78B00FD016664F6AA701363804C9B081B2299823878027003698FE99F9810E000C92F857010C0801F5D41081DCD650029285029185F7970E101E87D007D207D0018384008646582A804E78B28B9D090D0A85AD08A500AFD010AE5B564B8FD80384008646582AC678B2803FD010B65B564B8FD80384008646582A802E78B00FD0109E5B564B8FD80381041082FE61E8A10C00C646582A802E78B117D010A65B509E58F8A40900C8C0029A3110471036454012F004E032363704C0038E4782103B9ACA0015BEF2E1C95312C70559C705B1F2E1CA702082105FCC3D14218010C8CB055006CF1622FA0215CB6A14CB1F14CB3F21CF1601CF16CA0021FA02CA00C98100A0FB00E05F06840FF2F0002ACB3F22CF1658CF16CA0021FA02CA00C98100A0FB00AECABAD1' + code = "B5EE9C7241020A010001B4000114FF00F4A413F4BCF2C80B01020120020302014804050004F2300202CD0607002FA03859DA89A1F481F481F481F401A861A1F401F481F4006101F7D00E8698180B8D8492F82707D201876A2687D207D207D207D006A18116BA4E10159C71D991B1B2990E382C92F837028916382F970FA01698FC1080289C6C8895D7970FAE99F98FD2018201A642802E78B2801E78B00E78B00FD016664F6AA701363804C9B081B2299823878027003698FE99F9810E000C92F857010C0801F5D41081DCD650029285029185F7970E101E87D007D207D0018384008646582A804E78B28B9D090D0A85AD08A500AFD010AE5B564B8FD80384008646582AC678B2803FD010B65B564B8FD80384008646582A802E78B00FD0109E5B564B8FD80381041082FE61E8A10C00C646582A802E78B117D010A65B509E58F8A40900C8C0029A3110471036454012F004E032363704C0038E4782103B9ACA0015BEF2E1C95312C70559C705B1F2E1CA702082105FCC3D14218010C8CB055006CF1622FA0215CB6A14CB1F14CB3F21CF1601CF16CA0021FA02CA00C98100A0FB00E05F06840FF2F0002ACB3F22CF1658CF16CA0021FA02CA00C98100A0FB00AECABAD1" def __init__(self, **kwargs): - self.code = kwargs.get('code') or self.code + self.code = kwargs.get("code") or self.code kwargs["code"] = Cell.one_from_boc(self.code) super().__init__(**kwargs) def create_data_cell(self) -> Cell: cell = Cell() - cell.bits.write_address(self.options['marketplace_address']) - cell.bits.write_address(self.options['nft_address']) + cell.bits.write_address(self.options["marketplace_address"]) + cell.bits.write_address(self.options["nft_address"]) cell.bits.write_address(None) # nft_owner_address - cell.bits.write_grams(self.options['full_price']) + cell.bits.write_grams(self.options["full_price"]) fees_cell = Cell() - fees_cell.bits.write_coins(self.options['marketplace_fee']) - fees_cell.bits.write_address(self.options['royalty_address']) - fees_cell.bits.write_coins(self.options['royalty_amount']) + fees_cell.bits.write_coins(self.options["marketplace_fee"]) + fees_cell.bits.write_address(self.options["royalty_address"]) + fees_cell.bits.write_coins(self.options["royalty_amount"]) cell.refs.append(fees_cell) return cell @@ -31,5 +29,3 @@ def create_cancel_body(self, query_id: int = 0) -> Cell: cell.bits.write_uint(3, 32) # cancel OP-code cell.bits.write_uint(query_id, 64) return cell - - diff --git a/tonsdk/contract/token/nft/nft_utils.py b/tonsdk_ng/contract/token/nft/nft_utils.py similarity index 83% rename from tonsdk/contract/token/nft/nft_utils.py rename to tonsdk_ng/contract/token/nft/nft_utils.py index 299eb48..b23b805 100644 --- a/tonsdk/contract/token/nft/nft_utils.py +++ b/tonsdk_ng/contract/token/nft/nft_utils.py @@ -9,7 +9,7 @@ def serialize_uri(uri): - return urllib.parse.quote(uri, safe='~@#$&()*!+=:;,?/\'').encode() + return urllib.parse.quote(uri, safe="~@#$&()*!+=:;,?/'").encode() def parse_uri(uri): @@ -24,7 +24,7 @@ def create_offchain_uri_cell(uri): def parse_offchain_uri_cell(cell): - assert cell.bits[0] == OFFCHAIN_CONTENT_PREFIX, 'Invalid offchain uri cell' + assert cell.bits[0] == OFFCHAIN_CONTENT_PREFIX, "Invalid offchain uri cell" length = 0 c = cell while c: diff --git a/tonsdk/contract/wallet/__init__.py b/tonsdk_ng/contract/wallet/__init__.py similarity index 56% rename from tonsdk/contract/wallet/__init__.py rename to tonsdk_ng/contract/wallet/__init__.py index 9b58968..4924905 100644 --- a/tonsdk/contract/wallet/__init__.py +++ b/tonsdk_ng/contract/wallet/__init__.py @@ -1,25 +1,28 @@ from enum import Enum from typing import List, Optional, Tuple -from ._wallet_contract import SendModeEnum -from ._wallet_contract import WalletContract +from ...crypto import mnemonic_is_valid, mnemonic_new, mnemonic_to_wallet_key +from ...crypto.exceptions import InvalidMnemonicsError +from ._highload_wallet_contract import HighloadWalletV2Contract +from ._multisig_wallet_contract import ( + MultiSigOrder, + MultiSigOrderBuilder, + MultiSigWallet, +) +from ._wallet_contract import SendModeEnum, WalletContract from ._wallet_contract_v2 import WalletV2ContractR1, WalletV2ContractR2 from ._wallet_contract_v3 import WalletV3ContractR1, WalletV3ContractR2 from ._wallet_contract_v4 import WalletV4ContractR1, WalletV4ContractR2 -from ._highload_wallet_contract import HighloadWalletV2Contract -from ._multisig_wallet_contract import MultiSigWallet, MultiSigOrder, MultiSigOrderBuilder -from ...crypto import mnemonic_new, mnemonic_to_wallet_key, mnemonic_is_valid -from ...crypto.exceptions import InvalidMnemonicsError class WalletVersionEnum(str, Enum): - v2r1 = 'v2r1' - v2r2 = 'v2r2' - v3r1 = 'v3r1' - v3r2 = 'v3r2' - v4r1 = 'v4r1' - v4r2 = 'v4r2' - hv2 = 'hv2' + v2r1 = "v2r1" + v2r2 = "v2r2" + v3r1 = "v3r1" + v3r2 = "v3r2" + v4r1 = "v4r1" + v4r2 = "v4r2" + hv2 = "hv2" class Wallets: @@ -31,25 +34,36 @@ class Wallets: WalletVersionEnum.v3r2: WalletV3ContractR2, WalletVersionEnum.v4r1: WalletV4ContractR1, WalletVersionEnum.v4r2: WalletV4ContractR2, - WalletVersionEnum.hv2: HighloadWalletV2Contract + WalletVersionEnum.hv2: HighloadWalletV2Contract, } @classmethod - def create(cls, version: WalletVersionEnum, workchain: int, - password: Optional[str] = None, **kwargs) -> Tuple[List[str], bytes, bytes, WalletContract]: + def create( + cls, + version: WalletVersionEnum, + workchain: int, + password: Optional[str] = None, + **kwargs, + ) -> Tuple[List[str], bytes, bytes, WalletContract]: """ :rtype: (List[str](mnemonics), bytes(public_key), bytes(private_key), WalletContract(wallet)) """ mnemonics = mnemonic_new(password=password) pub_k, priv_k = mnemonic_to_wallet_key(mnemonics) wallet = cls.ALL[version]( - public_key=pub_k, private_key=priv_k, wc=workchain, **kwargs) + public_key=pub_k, private_key=priv_k, wc=workchain, **kwargs + ) return mnemonics, pub_k, priv_k, wallet @classmethod - def from_mnemonics(cls, mnemonics: List[str], version: WalletVersionEnum = default_version, - workchain: int = 0, **kwargs) -> Tuple[List[str], bytes, bytes, WalletContract]: + def from_mnemonics( + cls, + mnemonics: List[str], + version: WalletVersionEnum = default_version, + workchain: int = 0, + **kwargs, + ) -> Tuple[List[str], bytes, bytes, WalletContract]: """ :rtype: (List[str](mnemonics), bytes(public_key), bytes(private_key), WalletContract(wallet)) """ @@ -58,34 +72,41 @@ def from_mnemonics(cls, mnemonics: List[str], version: WalletVersionEnum = defau pub_k, priv_k = mnemonic_to_wallet_key(mnemonics) wallet = cls.ALL[version]( - public_key=pub_k, private_key=priv_k, wc=workchain, **kwargs) + public_key=pub_k, private_key=priv_k, wc=workchain, **kwargs + ) return mnemonics, pub_k, priv_k, wallet @classmethod - def to_addr_pk(cls, mnemonics: WalletContract, version: WalletVersionEnum = default_version, - workchain: int = 0, **kwargs) -> Tuple[bytes, bytes]: + def to_addr_pk( + cls, + mnemonics: WalletContract, + version: WalletVersionEnum = default_version, + workchain: int = 0, + **kwargs, + ) -> Tuple[bytes, bytes]: """ :rtype: (bytes(addr), bytes(pk) """ _mnemonics, _pub_k, priv_k, wallet = cls.from_mnemonics( - mnemonics, version, workchain, **kwargs) + mnemonics, version, workchain, **kwargs + ) return wallet.address.to_buffer(), priv_k[:32] __all__ = [ - 'WalletV2ContractR1', - 'WalletV2ContractR2', - 'WalletV3ContractR1', - 'WalletV3ContractR2', - 'WalletV4ContractR1', - 'WalletV4ContractR2', - 'WalletContract', - 'SendModeEnum', - 'WalletVersionEnum', - 'Wallets', - 'MultiSigWallet', - 'MultiSigOrder', - 'MultiSigOrderBuilder' + "WalletV2ContractR1", + "WalletV2ContractR2", + "WalletV3ContractR1", + "WalletV3ContractR2", + "WalletV4ContractR1", + "WalletV4ContractR2", + "WalletContract", + "SendModeEnum", + "WalletVersionEnum", + "Wallets", + "MultiSigWallet", + "MultiSigOrder", + "MultiSigOrderBuilder", ] diff --git a/tonsdk/contract/wallet/_highload_wallet_contract.py b/tonsdk_ng/contract/wallet/_highload_wallet_contract.py similarity index 68% rename from tonsdk/contract/wallet/_highload_wallet_contract.py rename to tonsdk_ng/contract/wallet/_highload_wallet_contract.py index 6902baf..fe76343 100644 --- a/tonsdk/contract/wallet/_highload_wallet_contract.py +++ b/tonsdk_ng/contract/wallet/_highload_wallet_contract.py @@ -1,22 +1,24 @@ -import time import decimal +import time -from .. import Contract -from ._wallet_contract import WalletContract from ...boc import Cell, begin_cell, begin_dict from ...utils import Address, sign_message +from .. import Contract +from ._wallet_contract import WalletContract class HighloadWalletContractBase(WalletContract): def create_data_cell(self): - return begin_cell() \ - .store_uint(self.options["wallet_id"], 32) \ - .store_uint(0, 64) \ - .store_bytes(self.options["public_key"]) \ - .store_maybe_ref(None) \ + return ( + begin_cell() + .store_uint(self.options["wallet_id"], 32) + .store_uint(0, 64) + .store_bytes(self.options["public_key"]) + .store_maybe_ref(None) .end_cell() + ) - def create_signing_message(self, query_id: int=0): + def create_signing_message(self, query_id: int = 0): message = begin_cell().store_uint(self.options["wallet_id"], 32) return message.store_uint(query_id, 64) @@ -30,7 +32,13 @@ def __init__(self, **kwargs) -> None: if "wallet_id" not in kwargs: self.options["wallet_id"] = 698983191 + self.options["wc"] - def create_transfer_message(self, recipients_list: list, query_id: int, timeout=60, dummy_signature=False): + def create_transfer_message( + self, + recipients_list: list, + query_id: int, + timeout=60, + dummy_signature=False, + ): if query_id < int(time.time() + timeout) << 32: query_id = int(time.time() + timeout) << 32 + query_id @@ -38,26 +46,29 @@ def create_transfer_message(self, recipients_list: list, query_id: int, timeout= recipients = begin_dict(16) for i, recipient in enumerate(recipients_list): payload_cell = Cell() - if recipient.get('payload'): - if type(recipient['payload']) == str: - if len(recipient['payload']) > 0: + if recipient.get("payload"): + if type(recipient["payload"]) == str: + if len(recipient["payload"]) > 0: payload_cell.bits.write_uint(0, 32) - payload_cell.bits.write_string(recipient['payload']) - elif hasattr(recipient['payload'], 'refs'): - payload_cell = recipient['payload'] + payload_cell.bits.write_string(recipient["payload"]) + elif hasattr(recipient["payload"], "refs"): + payload_cell = recipient["payload"] else: - payload_cell.bits.write_bytes(recipient['payload']) + payload_cell.bits.write_bytes(recipient["payload"]) order_header = Contract.create_internal_message_header( - Address(recipient['address']), decimal.Decimal(recipient['amount']) + Address(recipient["address"]), + decimal.Decimal(recipient["amount"]), ) order = Contract.create_common_msg_info( - order_header, recipient.get('state_init'), payload_cell + order_header, recipient.get("state_init"), payload_cell ) recipients.store_cell( - i, begin_cell() \ - .store_uint8(recipient.get('send_mode', 0)) \ - .store_ref(order).end_cell() + i, + begin_cell() + .store_uint8(recipient.get("send_mode", 0)) + .store_ref(order) + .end_cell(), ) signing_message.store_maybe_ref(recipients.end_cell()) @@ -66,8 +77,13 @@ def create_transfer_message(self, recipients_list: list, query_id: int, timeout= ) def create_external_message(self, signing_message, dummy_signature=False): - signature = bytes(64) if dummy_signature else sign_message( - bytes(signing_message.bytes_hash()), self.options['private_key']).signature + signature = ( + bytes(64) + if dummy_signature + else sign_message( + bytes(signing_message.bytes_hash()), self.options["private_key"] + ).signature + ) body = Cell() body.bits.write_bytes(signature) @@ -77,7 +93,8 @@ def create_external_message(self, signing_message, dummy_signature=False): self_address = self.address header = Contract.create_external_message_header(self_address) result_message = Contract.create_common_msg_info( - header, state_init, body) + header, state_init, body + ) return { "address": self_address, @@ -88,7 +105,7 @@ def create_external_message(self, signing_message, dummy_signature=False): "state_init": state_init, "code": code, "data": data, - "query_id": int.from_bytes(signing_message.bits.array[4:12], 'big') + "query_id": int.from_bytes(signing_message.bits.array[4:12], "big"), } def create_init_external_message(self, timeout=60): @@ -98,10 +115,14 @@ def create_init_external_message(self, timeout=60): code = create_state_init["code"] data = create_state_init["data"] - signing_message = self.create_signing_message(int(time.time() + timeout) << 32) \ - .store_maybe_ref(None).end_cell() + signing_message = ( + self.create_signing_message(int(time.time() + timeout) << 32) + .store_maybe_ref(None) + .end_cell() + ) signature = sign_message( - bytes(signing_message.bytes_hash()), self.options['private_key']).signature + bytes(signing_message.bytes_hash()), self.options["private_key"] + ).signature body = Cell() body.bits.write_bytes(signature) @@ -109,12 +130,12 @@ def create_init_external_message(self, timeout=60): header = Contract.create_external_message_header(address) external_message = Contract.create_common_msg_info( - header, state_init, body) + header, state_init, body + ) return { "address": address, "message": external_message, - "body": body, "signing_message": signing_message, "state_init": state_init, diff --git a/tonsdk/contract/wallet/_multisig_wallet_contract.py b/tonsdk_ng/contract/wallet/_multisig_wallet_contract.py similarity index 69% rename from tonsdk/contract/wallet/_multisig_wallet_contract.py rename to tonsdk_ng/contract/wallet/_multisig_wallet_contract.py index 9543eb7..be86710 100644 --- a/tonsdk/contract/wallet/_multisig_wallet_contract.py +++ b/tonsdk_ng/contract/wallet/_multisig_wallet_contract.py @@ -1,13 +1,12 @@ -import base64 -import time import decimal +import time from typing import Optional +from ...boc import Cell, begin_cell, begin_dict +from ...crypto import private_key_to_public_key, verify_sign +from ...utils import Address, sign_message from .. import Contract from ._wallet_contract import WalletContract -from ...boc import Cell, begin_cell, begin_dict, Builder -from ...utils import Address, sign_message -from ...crypto import private_key_to_public_key, verify_sign class MultiSigWalletContractBase(WalletContract): @@ -15,20 +14,23 @@ def create_data_cell(self): owners = begin_dict(8) for i in range(len(self.options["public_keys"])): owners.store_cell( - i, begin_cell() \ - .store_bytes(self.options["public_keys"][i]) \ - .store_bytes(b'\x00') \ - .end_cell() + i, + begin_cell() + .store_bytes(self.options["public_keys"][i]) + .store_bytes(b"\x00") + .end_cell(), ) - return begin_cell() \ - .store_uint(self.options["wallet_id"], 32) \ - .store_uint(len(self.options["public_keys"]), 8) \ - .store_uint(self.options["k"], 8) \ - .store_uint(0, 64) \ - .store_maybe_ref(owners.end_cell()) \ - .store_bit(0) \ + return ( + begin_cell() + .store_uint(self.options["wallet_id"], 32) + .store_uint(len(self.options["public_keys"]), 8) + .store_uint(self.options["k"], 8) + .store_uint(0, 64) + .store_maybe_ref(owners.end_cell()) + .store_bit(0) .end_cell() + ) class MultiSigOrder: @@ -38,19 +40,25 @@ def __init__(self, payload: Cell): def sign(self, owner_id: int, secret_key: bytes): signing_hash = self.payload.bytes_hash() - self.signatures[owner_id] = sign_message(bytes(signing_hash), secret_key).signature + self.signatures[owner_id] = sign_message( + bytes(signing_hash), secret_key + ).signature return signing_hash def add_signature(self, owner_id: int, signature: bytes, multisig_wallet): signing_hash = self.payload.bytes_hash() - if not verify_sign(public_key=multisig_wallet.options['public_keys'][owner_id], - signed_message=bytes(signing_hash), - signature=signature): - raise Exception('Invalid signature') + if not verify_sign( + public_key=multisig_wallet.options["public_keys"][owner_id], + signed_message=bytes(signing_hash), + signature=signature, + ): + raise Exception("Invalid signature") self.signatures[owner_id] = signature def union_signatures(self, other): - self.signatures = dict(list(self.signatures.items()) + list(other.signatures.items())) + self.signatures = dict( + list(self.signatures.items()) + list(other.signatures.items()) + ) def clear_signatures(self): self.signatures = {} @@ -59,50 +67,60 @@ def to_cell(self, owner_id: int): b = begin_cell().store_bit(0) for owner in self.signatures: signature = self.signatures[owner] - b = begin_cell() \ - .store_bit(1) \ + b = ( + begin_cell() + .store_bit(1) .store_ref( - begin_cell() \ - .store_bytes(signature) \ - .store_uint(owner, 8) \ - .store_cell(b.end_cell()) \ + begin_cell() + .store_bytes(signature) + .store_uint(owner, 8) + .store_cell(b.end_cell()) .end_cell() ) - return begin_cell() \ - .store_uint(owner_id, 8) \ - .store_cell(b.end_cell()) \ - .store_cell(self.payload) \ - .end_cell() + ) + return ( + begin_cell() + .store_uint(owner_id, 8) + .store_cell(b.end_cell()) + .store_cell(self.payload) + .end_cell() + ) class MultiSigOrderBuilder: def __init__(self, wallet_id, offset=7200, query_id: Optional[int] = None): self.wallet_id = wallet_id self.messages = begin_cell() - self.query_id = query_id if query_id is not None else self.generate_query_id(offset) + self.query_id = ( + query_id if query_id is not None else self.generate_query_id(offset) + ) - def add_message(self, to_addr, amount, payload="", send_mode=3, state_init=None): + def add_message( + self, to_addr, amount, payload="", send_mode=3, state_init=None + ): payload_cell = Cell() if payload: if type(payload) == str: if len(payload) > 0: payload_cell.bits.write_uint(0, 32) payload_cell.bits.write_string(payload) - elif hasattr(payload, 'refs'): + elif hasattr(payload, "refs"): payload_cell = payload else: payload_cell.bits.write_bytes(payload) order_header = Contract.create_internal_message_header( - Address(to_addr), decimal.Decimal(amount)) + Address(to_addr), decimal.Decimal(amount) + ) order = Contract.create_common_msg_info( - order_header, state_init, payload_cell) + order_header, state_init, payload_cell + ) return self.add_message_from_cell(order, send_mode) def add_message_from_cell(self, message: Cell, mode: int = 3): if len(self.messages.refs) >= 4: - raise Exception('only 4 refs are allowed') + raise Exception("only 4 refs are allowed") self.messages.store_uint(mode, 8) self.messages.store_ref(begin_cell().store_cell(message).end_cell()) return message @@ -111,11 +129,13 @@ def clear_messages(self): self.messages = begin_cell() def build(self): - return MultiSigOrder(begin_cell() \ - .store_uint(self.wallet_id, 32) \ - .store_uint(self.query_id, 64) \ - .store_cell(self.messages.end_cell()) \ - .end_cell()) + return MultiSigOrder( + begin_cell() + .store_uint(self.wallet_id, 32) + .store_uint(self.query_id, 64) + .store_cell(self.messages.end_cell()) + .end_cell() + ) @staticmethod def generate_query_id(offset): @@ -126,7 +146,7 @@ class MultiSigWallet(MultiSigWalletContractBase): def __init__(self, **kwargs) -> None: # https://github.com/ton-blockchain/multisig-contract/ # https://github.com/ton-core/ton/blob/master/src/multisig/MultisigWallet.ts - self.code = 'B5EE9C7201022B01000418000114FF00F4A413F4BCF2C80B010201200203020148040504DAF220C7008E8330DB3CE08308D71820F90101D307DB3C22C00013A1537178F40E6FA1F29FDB3C541ABAF910F2A006F40420F90101D31F5118BAF2AAD33F705301F00A01C20801830ABCB1F26853158040F40E6FA120980EA420C20AF2670EDFF823AA1F5340B9F2615423A3534E202321220202CC06070201200C0D02012008090201660A0B0003D1840223F2980BC7A0737D0986D9E52ED9E013C7A21C2125002D00A908B5D244A824C8B5D2A5C0B5007404FC02BA1B04A0004F085BA44C78081BA44C3800740835D2B0C026B500BC02F21633C5B332781C75C8F20073C5BD0032600201200E0F02012014150115BBED96D5034705520DB3C82A020148101102012012130173B11D7420C235C6083E404074C1E08075313B50F614C81E3D039BE87CA7F5C2FFD78C7E443CA82B807D01085BA4D6DC4CB83E405636CF0069006027003DAEDA80E800E800FA02017A0211FC8080FC80DD794FF805E47A0000E78B64C00017AE19573FC100D56676A1EC40020120161702012018190151B7255B678626466A4610081E81CDF431C24D845A4000331A61E62E005AE0261C0B6FEE1C0B77746E10230189B5599B6786ABE06FEDB1C6CA2270081E8F8DF4A411C4A05A400031C38410021AE424BAE064F6451613990039E2CA840090081E886052261C52261C52265C4036625CCD8A30230201201A1B0017B506B5CE104035599DA87B100201201C1D020399381E1F0111AC1A6D9E2F81B60940230015ADF94100CC9576A1EC1840010DA936CF0557C160230015ADDFDC20806AB33B50F6200220DB3C02F265F8005043714313DB3CED54232A000AD3FFD3073004A0DB3C2FAE5320B0F26212B102A425B3531CB9B0258100E1AA23A028BCB0F269820186A0F8010597021110023E3E308E8D11101FDB3C40D778F44310BD05E254165B5473E7561053DCDB3C54710A547ABC242528260020ED44D0D31FD307D307D33FF404F404D1005E018E1A30D20001F2A3D307D3075003D70120F90105F90115BAF2A45003E06C2121D74AAA0222D749BAF2AB70542013000C01C8CBFFCB0704D6DB3CED54F80F70256E5389BEB198106E102D50C75F078F1B30542403504DDB3C5055A046501049103A4B0953B9DB3C5054167FE2F800078325A18E2C268040F4966FA52094305303B9DE208E1638393908D2000197D3073016F007059130E27F080705926C31E2B3E630062A2728290060708E2903D08308D718D307F40430531678F40E6FA1F2A5D70BFF544544F910F2A6AE5220B15203BD14A1236EE66C2232007E5230BE8E205F03F8009322D74A9802D307D402FB0002E83270C8CA0040148040F44302F0078E1771C8CB0014CB0712CB0758CF0158CF1640138040F44301E201208E8A104510344300DB3CED54925F06E22A001CC8CB1FCB07CB07CB3FF400F400C9' + self.code = "B5EE9C7201022B01000418000114FF00F4A413F4BCF2C80B010201200203020148040504DAF220C7008E8330DB3CE08308D71820F90101D307DB3C22C00013A1537178F40E6FA1F29FDB3C541ABAF910F2A006F40420F90101D31F5118BAF2AAD33F705301F00A01C20801830ABCB1F26853158040F40E6FA120980EA420C20AF2670EDFF823AA1F5340B9F2615423A3534E202321220202CC06070201200C0D02012008090201660A0B0003D1840223F2980BC7A0737D0986D9E52ED9E013C7A21C2125002D00A908B5D244A824C8B5D2A5C0B5007404FC02BA1B04A0004F085BA44C78081BA44C3800740835D2B0C026B500BC02F21633C5B332781C75C8F20073C5BD0032600201200E0F02012014150115BBED96D5034705520DB3C82A020148101102012012130173B11D7420C235C6083E404074C1E08075313B50F614C81E3D039BE87CA7F5C2FFD78C7E443CA82B807D01085BA4D6DC4CB83E405636CF0069006027003DAEDA80E800E800FA02017A0211FC8080FC80DD794FF805E47A0000E78B64C00017AE19573FC100D56676A1EC40020120161702012018190151B7255B678626466A4610081E81CDF431C24D845A4000331A61E62E005AE0261C0B6FEE1C0B77746E10230189B5599B6786ABE06FEDB1C6CA2270081E8F8DF4A411C4A05A400031C38410021AE424BAE064F6451613990039E2CA840090081E886052261C52261C52265C4036625CCD8A30230201201A1B0017B506B5CE104035599DA87B100201201C1D020399381E1F0111AC1A6D9E2F81B60940230015ADF94100CC9576A1EC1840010DA936CF0557C160230015ADDFDC20806AB33B50F6200220DB3C02F265F8005043714313DB3CED54232A000AD3FFD3073004A0DB3C2FAE5320B0F26212B102A425B3531CB9B0258100E1AA23A028BCB0F269820186A0F8010597021110023E3E308E8D11101FDB3C40D778F44310BD05E254165B5473E7561053DCDB3C54710A547ABC242528260020ED44D0D31FD307D307D33FF404F404D1005E018E1A30D20001F2A3D307D3075003D70120F90105F90115BAF2A45003E06C2121D74AAA0222D749BAF2AB70542013000C01C8CBFFCB0704D6DB3CED54F80F70256E5389BEB198106E102D50C75F078F1B30542403504DDB3C5055A046501049103A4B0953B9DB3C5054167FE2F800078325A18E2C268040F4966FA52094305303B9DE208E1638393908D2000197D3073016F007059130E27F080705926C31E2B3E630062A2728290060708E2903D08308D718D307F40430531678F40E6FA1F2A5D70BFF544544F910F2A6AE5220B15203BD14A1236EE66C2232007E5230BE8E205F03F8009322D74A9802D307D402FB0002E83270C8CA0040148040F44302F0078E1771C8CB0014CB0712CB0758CF0158CF1640138040F44301E201208E8A104510344300DB3CED54925F06E22A001CC8CB1FCB07CB07CB3FF400F400C9" kwargs["code"] = Cell.one_from_boc(self.code) super().__init__(**kwargs) if "wallet_id" not in kwargs: @@ -134,10 +154,12 @@ def __init__(self, **kwargs) -> None: def get_owner_id_by_public_key(self, public_key: bytes): if public_key not in self.options["public_keys"]: - raise Exception('public key is not an owner') + raise Exception("public key is not an owner") return list(self.options["public_keys"]).index(public_key) - def create_transfer_message(self, order: MultiSigOrder, private_key: bytes, dummy_signature=False): + def create_transfer_message( + self, order: MultiSigOrder, private_key: bytes, dummy_signature=False + ): public_key = private_key_to_public_key(private_key) owner_id = self.get_owner_id_by_public_key(public_key) signing_message = order.to_cell(owner_id) @@ -146,9 +168,16 @@ def create_transfer_message(self, order: MultiSigOrder, private_key: bytes, dumm signing_message, private_key, dummy_signature ) - def create_external_message(self, signing_message, private_key, dummy_signature=False): - signature = bytes(64) if dummy_signature else sign_message( - bytes(signing_message.bytes_hash()), private_key).signature + def create_external_message( + self, signing_message, private_key, dummy_signature=False + ): + signature = ( + bytes(64) + if dummy_signature + else sign_message( + bytes(signing_message.bytes_hash()), private_key + ).signature + ) body = Cell() body.bits.write_bytes(signature) @@ -158,7 +187,8 @@ def create_external_message(self, signing_message, private_key, dummy_signature= self_address = self.address header = Contract.create_external_message_header(self_address) result_message = Contract.create_common_msg_info( - header, state_init, body) + header, state_init, body + ) return { "address": self_address, @@ -169,7 +199,7 @@ def create_external_message(self, signing_message, private_key, dummy_signature= "state_init": state_init, "code": code, "data": data, - "query_id": int.from_bytes(signing_message.bits.array[4:12], 'big') + "query_id": int.from_bytes(signing_message.bits.array[4:12], "big"), } def create_init_external_message(self): @@ -182,7 +212,9 @@ def create_init_external_message(self): body = Cell() header = Contract.create_external_message_header(address) - external_message = Contract.create_common_msg_info(header, state_init, body) + external_message = Contract.create_common_msg_info( + header, state_init, body + ) return { "address": address, diff --git a/tonsdk/contract/wallet/_wallet_contract.py b/tonsdk_ng/contract/wallet/_wallet_contract.py similarity index 72% rename from tonsdk/contract/wallet/_wallet_contract.py rename to tonsdk_ng/contract/wallet/_wallet_contract.py index 4dfc67a..2300ae7 100644 --- a/tonsdk/contract/wallet/_wallet_contract.py +++ b/tonsdk_ng/contract/wallet/_wallet_contract.py @@ -2,9 +2,9 @@ from enum import Enum from typing import Union -from .. import Contract from ...boc import Cell from ...utils import Address, sign_message +from .. import Contract class SendModeEnum(int, Enum): @@ -20,9 +20,13 @@ def __str__(self) -> str: class WalletContract(Contract): def __init__(self, **kwargs): - if (("public_key" not in kwargs or "private_key" not in kwargs) and "address" not in kwargs) and 'public_keys' not in kwargs: + if ( + ("public_key" not in kwargs or "private_key" not in kwargs) + and "address" not in kwargs + ) and "public_keys" not in kwargs: raise Exception( - "WalletContract required publicKey or address in options") + "WalletContract required publicKey or address in options" + ) super().__init__(**kwargs) def create_data_cell(self): @@ -37,13 +41,16 @@ def create_signing_message(self, seqno=None): cell.bits.write_uint(seqno, 32) return cell - def create_transfer_message(self, - to_addr: str, - amount: int, - seqno: int, - payload: Union[Cell, str, bytes, None] = None, - send_mode=SendModeEnum.ignore_errors | SendModeEnum.pay_gas_separately, - dummy_signature=False, state_init=None): + def create_transfer_message( + self, + to_addr: str, + amount: int, + seqno: int, + payload: Union[Cell, str, bytes, None] = None, + send_mode=SendModeEnum.ignore_errors | SendModeEnum.pay_gas_separately, + dummy_signature=False, + state_init=None, + ): payload_cell = Cell() if payload: if isinstance(payload, str): @@ -55,18 +62,29 @@ def create_transfer_message(self, payload_cell.bits.write_bytes(payload) order_header = Contract.create_internal_message_header( - Address(to_addr), decimal.Decimal(amount)) + Address(to_addr), decimal.Decimal(amount) + ) order = Contract.create_common_msg_info( - order_header, state_init, payload_cell) + order_header, state_init, payload_cell + ) signing_message = self.create_signing_message(seqno) signing_message.bits.write_uint8(send_mode) signing_message.refs.append(order) - return self.create_external_message(signing_message, seqno, dummy_signature) - - def create_external_message(self, signing_message, seqno, dummy_signature=False): - signature = bytes(64) if dummy_signature else sign_message( - bytes(signing_message.bytes_hash()), self.options['private_key']).signature + return self.create_external_message( + signing_message, seqno, dummy_signature + ) + + def create_external_message( + self, signing_message, seqno, dummy_signature=False + ): + signature = ( + bytes(64) + if dummy_signature + else sign_message( + bytes(signing_message.bytes_hash()), self.options["private_key"] + ).signature + ) body = Cell() body.bits.write_bytes(signature) @@ -83,7 +101,8 @@ def create_external_message(self, signing_message, seqno, dummy_signature=False) self_address = self.address header = Contract.create_external_message_header(self_address) result_message = Contract.create_common_msg_info( - header, state_init, body) + header, state_init, body + ) return { "address": self_address, @@ -105,7 +124,8 @@ def create_init_external_message(self): signing_message = self.create_signing_message() signature = sign_message( - bytes(signing_message.bytes_hash()), self.options['private_key']).signature + bytes(signing_message.bytes_hash()), self.options["private_key"] + ).signature body = Cell() body.bits.write_bytes(signature) @@ -113,12 +133,12 @@ def create_init_external_message(self): header = Contract.create_external_message_header(address) external_message = Contract.create_common_msg_info( - header, state_init, body) + header, state_init, body + ) return { "address": address, "message": external_message, - "body": body, "signing_message": signing_message, "state_init": state_init, diff --git a/tonsdk/contract/wallet/_wallet_contract_v2.py b/tonsdk_ng/contract/wallet/_wallet_contract_v2.py similarity index 100% rename from tonsdk/contract/wallet/_wallet_contract_v2.py rename to tonsdk_ng/contract/wallet/_wallet_contract_v2.py index 4d84a50..764ff41 100644 --- a/tonsdk/contract/wallet/_wallet_contract_v2.py +++ b/tonsdk_ng/contract/wallet/_wallet_contract_v2.py @@ -1,7 +1,7 @@ import time -from ._wallet_contract import WalletContract from ...boc import Cell +from ._wallet_contract import WalletContract class WalletV2ContractBase(WalletContract): diff --git a/tonsdk/contract/wallet/_wallet_contract_v3.py b/tonsdk_ng/contract/wallet/_wallet_contract_v3.py similarity index 100% rename from tonsdk/contract/wallet/_wallet_contract_v3.py rename to tonsdk_ng/contract/wallet/_wallet_contract_v3.py index 5d6317b..639413e 100644 --- a/tonsdk/contract/wallet/_wallet_contract_v3.py +++ b/tonsdk_ng/contract/wallet/_wallet_contract_v3.py @@ -1,7 +1,7 @@ import time -from ._wallet_contract import WalletContract from ...boc import Cell +from ._wallet_contract import WalletContract class WalletV3ContractBase(WalletContract): diff --git a/tonsdk/contract/wallet/_wallet_contract_v4.py b/tonsdk_ng/contract/wallet/_wallet_contract_v4.py similarity index 100% rename from tonsdk/contract/wallet/_wallet_contract_v4.py rename to tonsdk_ng/contract/wallet/_wallet_contract_v4.py index 5e52377..7f4bcfb 100644 --- a/tonsdk/contract/wallet/_wallet_contract_v4.py +++ b/tonsdk_ng/contract/wallet/_wallet_contract_v4.py @@ -1,7 +1,7 @@ import time -from ._wallet_contract import WalletContract from ...boc import Cell +from ._wallet_contract import WalletContract class WalletV4ContractBase(WalletContract): diff --git a/tonsdk_ng/crypto/__init__.py b/tonsdk_ng/crypto/__init__.py new file mode 100644 index 0000000..254b0eb --- /dev/null +++ b/tonsdk_ng/crypto/__init__.py @@ -0,0 +1,14 @@ +from ._keystore import generate_keystore_key, generate_new_keystore +from ._mnemonic import mnemonic_is_valid, mnemonic_new, mnemonic_to_wallet_key +from ._utils import private_key_to_public_key, verify_sign + +__all__ = [ + "mnemonic_new", + "mnemonic_to_wallet_key", + "mnemonic_is_valid", + "generate_new_keystore", + "generate_keystore_key", + "private_key_to_public_key", + "verify_sign", + "generate_key_pair", +] diff --git a/tonsdk/crypto/_keystore.py b/tonsdk_ng/crypto/_keystore.py similarity index 89% rename from tonsdk/crypto/_keystore.py rename to tonsdk_ng/crypto/_keystore.py index b773e12..4a2ea6f 100644 --- a/tonsdk/crypto/_keystore.py +++ b/tonsdk_ng/crypto/_keystore.py @@ -9,7 +9,7 @@ def generate_keystore_key(password: str, salt: bytes) -> Tuple[bytes, bytes]: """ :rtype: (bytes(public_key), bytes(secret_key)) """ - secret = pbkdf2_hmac("sha512", password.encode('utf-8'), salt, 400_000, 32) + secret = pbkdf2_hmac("sha512", password.encode("utf-8"), salt, 400_000, 32) return crypto_box_seed_keypair(secret) diff --git a/tonsdk/crypto/_mnemonic.py b/tonsdk_ng/crypto/_mnemonic.py similarity index 63% rename from tonsdk/crypto/_mnemonic.py rename to tonsdk_ng/crypto/_mnemonic.py index 6980a2f..5552085 100644 --- a/tonsdk/crypto/_mnemonic.py +++ b/tonsdk_ng/crypto/_mnemonic.py @@ -10,31 +10,41 @@ def mnemonic_is_valid(mnemo_words: List[str]) -> bool: - return len(mnemo_words) == 24 and is_basic_seed(mnemonic_to_entropy(mnemo_words)) + return len(mnemo_words) == 24 and is_basic_seed( + mnemonic_to_entropy(mnemo_words) + ) def mnemonic_to_entropy(mnemo_words: List[str], password: Optional[str] = None): # TODO: implement password - sign = hmac.new((" ".join(mnemo_words)).encode( - 'utf-8'), bytes(0), hashlib.sha512).digest() + sign = hmac.new( + (" ".join(mnemo_words)).encode("utf-8"), bytes(0), hashlib.sha512 + ).digest() return sign -def mnemonic_to_seed(mnemo_words: List[str], seed: str, password: Optional[str] = None): +def mnemonic_to_seed( + mnemo_words: List[str], seed: str, password: Optional[str] = None +): entropy = mnemonic_to_entropy(mnemo_words, password) return hashlib.pbkdf2_hmac("sha512", entropy, seed, PBKDF_ITERATIONS) -def mnemonic_to_private_key(mnemo_words: List[str], password: Optional[str] = None) -> Tuple[bytes, bytes]: +def mnemonic_to_private_key( + mnemo_words: List[str], password: Optional[str] = None +) -> Tuple[bytes, bytes]: """ :rtype: (bytes(public_key), bytes(secret_key)) """ seed = mnemonic_to_seed( - mnemo_words, 'TON default seed'.encode('utf-8'), password) + mnemo_words, "TON default seed".encode("utf-8"), password + ) return crypto_sign_seed_keypair(seed[:32]) -def mnemonic_to_wallet_key(mnemo_words: List[str], password: Optional[str] = None) -> Tuple[bytes, bytes]: +def mnemonic_to_wallet_key( + mnemo_words: List[str], password: Optional[str] = None +) -> Tuple[bytes, bytes]: """ :rtype: (bytes(public_key), bytes(secret_key)) """ @@ -42,7 +52,9 @@ def mnemonic_to_wallet_key(mnemo_words: List[str], password: Optional[str] = Non return crypto_sign_seed_keypair(priv_k[:32]) -def mnemonic_new(words_count: int = 24, password: Optional[str] = None) -> List[str]: +def mnemonic_new( + words_count: int = 24, password: Optional[str] = None +) -> List[str]: while True: mnemo_arr = [] diff --git a/tonsdk/crypto/_settings.py b/tonsdk_ng/crypto/_settings.py similarity index 100% rename from tonsdk/crypto/_settings.py rename to tonsdk_ng/crypto/_settings.py diff --git a/tonsdk/crypto/_utils.py b/tonsdk_ng/crypto/_utils.py similarity index 89% rename from tonsdk/crypto/_utils.py rename to tonsdk_ng/crypto/_utils.py index 92bca58..f65adeb 100644 --- a/tonsdk/crypto/_utils.py +++ b/tonsdk_ng/crypto/_utils.py @@ -3,11 +3,11 @@ import os from hashlib import pbkdf2_hmac -from ._settings import PBKDF_ITERATIONS - from nacl.bindings import crypto_sign_ed25519_sk_to_pk from nacl.signing import VerifyKey, exc +from ._settings import PBKDF_ITERATIONS + def get_secure_random_number(min_v, max_v): range_betw = max_v - min_v @@ -33,8 +33,12 @@ def get_secure_random_number(min_v, max_v): def is_basic_seed(entropy): - seed = pbkdf2_hmac("sha512", entropy, 'TON seed version'.encode( - 'utf-8'), max(1, math.floor(PBKDF_ITERATIONS / 256))) + seed = pbkdf2_hmac( + "sha512", + entropy, + "TON seed version".encode("utf-8"), + max(1, math.floor(PBKDF_ITERATIONS / 256)), + ) return seed[0] == 0 diff --git a/tonsdk/crypto/bip39/__init__.py b/tonsdk_ng/crypto/bip39/__init__.py similarity index 58% rename from tonsdk/crypto/bip39/__init__.py rename to tonsdk_ng/crypto/bip39/__init__.py index 2210d46..73ae6a6 100644 --- a/tonsdk/crypto/bip39/__init__.py +++ b/tonsdk_ng/crypto/bip39/__init__.py @@ -1,5 +1,3 @@ from ._english import words as english -__all__ = [ - 'english' -] +__all__ = ["english"] diff --git a/tonsdk_ng/crypto/bip39/_english.py b/tonsdk_ng/crypto/bip39/_english.py new file mode 100644 index 0000000..abed149 --- /dev/null +++ b/tonsdk_ng/crypto/bip39/_english.py @@ -0,0 +1,2050 @@ +words = [ + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "absurd", + "abuse", + "access", + "accident", + "account", + "accuse", + "achieve", + "acid", + "acoustic", + "acquire", + "across", + "act", + "action", + "actor", + "actress", + "actual", + "adapt", + "add", + "addict", + "address", + "adjust", + "admit", + "adult", + "advance", + "advice", + "aerobic", + "affair", + "afford", + "afraid", + "again", + "age", + "agent", + "agree", + "ahead", + "aim", + "air", + "airport", + "aisle", + "alarm", + "album", + "alcohol", + "alert", + "alien", + "all", + "alley", + "allow", + "almost", + "alone", + "alpha", + "already", + "also", + "alter", + "always", + "amateur", + "amazing", + "among", + "amount", + "amused", + "analyst", + "anchor", + "ancient", + "anger", + "angle", + "angry", + "animal", + "ankle", + "announce", + "annual", + "another", + "answer", + "antenna", + "antique", + "anxiety", + "any", + "apart", + "apology", + "appear", + "apple", + "approve", + "april", + "arch", + "arctic", + "area", + "arena", + "argue", + "arm", + "armed", + "armor", + "army", + "around", + "arrange", + "arrest", + "arrive", + "arrow", + "art", + "artefact", + "artist", + "artwork", + "ask", + "aspect", + "assault", + "asset", + "assist", + "assume", + "asthma", + "athlete", + "atom", + "attack", + "attend", + "attitude", + "attract", + "auction", + "audit", + "august", + "aunt", + "author", + "auto", + "autumn", + "average", + "avocado", + "avoid", + "awake", + "aware", + "away", + "awesome", + "awful", + "awkward", + "axis", + "baby", + "bachelor", + "bacon", + "badge", + "bag", + "balance", + "balcony", + "ball", + "bamboo", + "banana", + "banner", + "bar", + "barely", + "bargain", + "barrel", + "base", + "basic", + "basket", + "battle", + "beach", + "bean", + "beauty", + "because", + "become", + "beef", + "before", + "begin", + "behave", + "behind", + "believe", + "below", + "belt", + "bench", + "benefit", + "best", + "betray", + "better", + "between", + "beyond", + "bicycle", + "bid", + "bike", + "bind", + "biology", + "bird", + "birth", + "bitter", + "black", + "blade", + "blame", + "blanket", + "blast", + "bleak", + "bless", + "blind", + "blood", + "blossom", + "blouse", + "blue", + "blur", + "blush", + "board", + "boat", + "body", + "boil", + "bomb", + "bone", + "bonus", + "book", + "boost", + "border", + "boring", + "borrow", + "boss", + "bottom", + "bounce", + "box", + "boy", + "bracket", + "brain", + "brand", + "brass", + "brave", + "bread", + "breeze", + "brick", + "bridge", + "brief", + "bright", + "bring", + "brisk", + "broccoli", + "broken", + "bronze", + "broom", + "brother", + "brown", + "brush", + "bubble", + "buddy", + "budget", + "buffalo", + "build", + "bulb", + "bulk", + "bullet", + "bundle", + "bunker", + "burden", + "burger", + "burst", + "bus", + "business", + "busy", + "butter", + "buyer", + "buzz", + "cabbage", + "cabin", + "cable", + "cactus", + "cage", + "cake", + "call", + "calm", + "camera", + "camp", + "can", + "canal", + "cancel", + "candy", + "cannon", + "canoe", + "canvas", + "canyon", + "capable", + "capital", + "captain", + "car", + "carbon", + "card", + "cargo", + "carpet", + "carry", + "cart", + "case", + "cash", + "casino", + "castle", + "casual", + "cat", + "catalog", + "catch", + "category", + "cattle", + "caught", + "cause", + "caution", + "cave", + "ceiling", + "celery", + "cement", + "census", + "century", + "cereal", + "certain", + "chair", + "chalk", + "champion", + "change", + "chaos", + "chapter", + "charge", + "chase", + "chat", + "cheap", + "check", + "cheese", + "chef", + "cherry", + "chest", + "chicken", + "chief", + "child", + "chimney", + "choice", + "choose", + "chronic", + "chuckle", + "chunk", + "churn", + "cigar", + "cinnamon", + "circle", + "citizen", + "city", + "civil", + "claim", + "clap", + "clarify", + "claw", + "clay", + "clean", + "clerk", + "clever", + "click", + "client", + "cliff", + "climb", + "clinic", + "clip", + "clock", + "clog", + "close", + "cloth", + "cloud", + "clown", + "club", + "clump", + "cluster", + "clutch", + "coach", + "coast", + "coconut", + "code", + "coffee", + "coil", + "coin", + "collect", + "color", + "column", + "combine", + "come", + "comfort", + "comic", + "common", + "company", + "concert", + "conduct", + "confirm", + "congress", + "connect", + "consider", + "control", + "convince", + "cook", + "cool", + "copper", + "copy", + "coral", + "core", + "corn", + "correct", + "cost", + "cotton", + "couch", + "country", + "couple", + "course", + "cousin", + "cover", + "coyote", + "crack", + "cradle", + "craft", + "cram", + "crane", + "crash", + "crater", + "crawl", + "crazy", + "cream", + "credit", + "creek", + "crew", + "cricket", + "crime", + "crisp", + "critic", + "crop", + "cross", + "crouch", + "crowd", + "crucial", + "cruel", + "cruise", + "crumble", + "crunch", + "crush", + "cry", + "crystal", + "cube", + "culture", + "cup", + "cupboard", + "curious", + "current", + "curtain", + "curve", + "cushion", + "custom", + "cute", + "cycle", + "dad", + "damage", + "damp", + "dance", + "danger", + "daring", + "dash", + "daughter", + "dawn", + "day", + "deal", + "debate", + "debris", + "decade", + "december", + "decide", + "decline", + "decorate", + "decrease", + "deer", + "defense", + "define", + "defy", + "degree", + "delay", + "deliver", + "demand", + "demise", + "denial", + "dentist", + "deny", + "depart", + "depend", + "deposit", + "depth", + "deputy", + "derive", + "describe", + "desert", + "design", + "desk", + "despair", + "destroy", + "detail", + "detect", + "develop", + "device", + "devote", + "diagram", + "dial", + "diamond", + "diary", + "dice", + "diesel", + "diet", + "differ", + "digital", + "dignity", + "dilemma", + "dinner", + "dinosaur", + "direct", + "dirt", + "disagree", + "discover", + "disease", + "dish", + "dismiss", + "disorder", + "display", + "distance", + "divert", + "divide", + "divorce", + "dizzy", + "doctor", + "document", + "dog", + "doll", + "dolphin", + "domain", + "donate", + "donkey", + "donor", + "door", + "dose", + "double", + "dove", + "draft", + "dragon", + "drama", + "drastic", + "draw", + "dream", + "dress", + "drift", + "drill", + "drink", + "drip", + "drive", + "drop", + "drum", + "dry", + "duck", + "dumb", + "dune", + "during", + "dust", + "dutch", + "duty", + "dwarf", + "dynamic", + "eager", + "eagle", + "early", + "earn", + "earth", + "easily", + "east", + "easy", + "echo", + "ecology", + "economy", + "edge", + "edit", + "educate", + "effort", + "egg", + "eight", + "either", + "elbow", + "elder", + "electric", + "elegant", + "element", + "elephant", + "elevator", + "elite", + "else", + "embark", + "embody", + "embrace", + "emerge", + "emotion", + "employ", + "empower", + "empty", + "enable", + "enact", + "end", + "endless", + "endorse", + "enemy", + "energy", + "enforce", + "engage", + "engine", + "enhance", + "enjoy", + "enlist", + "enough", + "enrich", + "enroll", + "ensure", + "enter", + "entire", + "entry", + "envelope", + "episode", + "equal", + "equip", + "era", + "erase", + "erode", + "erosion", + "error", + "erupt", + "escape", + "essay", + "essence", + "estate", + "eternal", + "ethics", + "evidence", + "evil", + "evoke", + "evolve", + "exact", + "example", + "excess", + "exchange", + "excite", + "exclude", + "excuse", + "execute", + "exercise", + "exhaust", + "exhibit", + "exile", + "exist", + "exit", + "exotic", + "expand", + "expect", + "expire", + "explain", + "expose", + "express", + "extend", + "extra", + "eye", + "eyebrow", + "fabric", + "face", + "faculty", + "fade", + "faint", + "faith", + "fall", + "false", + "fame", + "family", + "famous", + "fan", + "fancy", + "fantasy", + "farm", + "fashion", + "fat", + "fatal", + "father", + "fatigue", + "fault", + "favorite", + "feature", + "february", + "federal", + "fee", + "feed", + "feel", + "female", + "fence", + "festival", + "fetch", + "fever", + "few", + "fiber", + "fiction", + "field", + "figure", + "file", + "film", + "filter", + "final", + "find", + "fine", + "finger", + "finish", + "fire", + "firm", + "first", + "fiscal", + "fish", + "fit", + "fitness", + "fix", + "flag", + "flame", + "flash", + "flat", + "flavor", + "flee", + "flight", + "flip", + "float", + "flock", + "floor", + "flower", + "fluid", + "flush", + "fly", + "foam", + "focus", + "fog", + "foil", + "fold", + "follow", + "food", + "foot", + "force", + "forest", + "forget", + "fork", + "fortune", + "forum", + "forward", + "fossil", + "foster", + "found", + "fox", + "fragile", + "frame", + "frequent", + "fresh", + "friend", + "fringe", + "frog", + "front", + "frost", + "frown", + "frozen", + "fruit", + "fuel", + "fun", + "funny", + "furnace", + "fury", + "future", + "gadget", + "gain", + "galaxy", + "gallery", + "game", + "gap", + "garage", + "garbage", + "garden", + "garlic", + "garment", + "gas", + "gasp", + "gate", + "gather", + "gauge", + "gaze", + "general", + "genius", + "genre", + "gentle", + "genuine", + "gesture", + "ghost", + "giant", + "gift", + "giggle", + "ginger", + "giraffe", + "girl", + "give", + "glad", + "glance", + "glare", + "glass", + "glide", + "glimpse", + "globe", + "gloom", + "glory", + "glove", + "glow", + "glue", + "goat", + "goddess", + "gold", + "good", + "goose", + "gorilla", + "gospel", + "gossip", + "govern", + "gown", + "grab", + "grace", + "grain", + "grant", + "grape", + "grass", + "gravity", + "great", + "green", + "grid", + "grief", + "grit", + "grocery", + "group", + "grow", + "grunt", + "guard", + "guess", + "guide", + "guilt", + "guitar", + "gun", + "gym", + "habit", + "hair", + "half", + "hammer", + "hamster", + "hand", + "happy", + "harbor", + "hard", + "harsh", + "harvest", + "hat", + "have", + "hawk", + "hazard", + "head", + "health", + "heart", + "heavy", + "hedgehog", + "height", + "hello", + "helmet", + "help", + "hen", + "hero", + "hidden", + "high", + "hill", + "hint", + "hip", + "hire", + "history", + "hobby", + "hockey", + "hold", + "hole", + "holiday", + "hollow", + "home", + "honey", + "hood", + "hope", + "horn", + "horror", + "horse", + "hospital", + "host", + "hotel", + "hour", + "hover", + "hub", + "huge", + "human", + "humble", + "humor", + "hundred", + "hungry", + "hunt", + "hurdle", + "hurry", + "hurt", + "husband", + "hybrid", + "ice", + "icon", + "idea", + "identify", + "idle", + "ignore", + "ill", + "illegal", + "illness", + "image", + "imitate", + "immense", + "immune", + "impact", + "impose", + "improve", + "impulse", + "inch", + "include", + "income", + "increase", + "index", + "indicate", + "indoor", + "industry", + "infant", + "inflict", + "inform", + "inhale", + "inherit", + "initial", + "inject", + "injury", + "inmate", + "inner", + "innocent", + "input", + "inquiry", + "insane", + "insect", + "inside", + "inspire", + "install", + "intact", + "interest", + "into", + "invest", + "invite", + "involve", + "iron", + "island", + "isolate", + "issue", + "item", + "ivory", + "jacket", + "jaguar", + "jar", + "jazz", + "jealous", + "jeans", + "jelly", + "jewel", + "job", + "join", + "joke", + "journey", + "joy", + "judge", + "juice", + "jump", + "jungle", + "junior", + "junk", + "just", + "kangaroo", + "keen", + "keep", + "ketchup", + "key", + "kick", + "kid", + "kidney", + "kind", + "kingdom", + "kiss", + "kit", + "kitchen", + "kite", + "kitten", + "kiwi", + "knee", + "knife", + "knock", + "know", + "lab", + "label", + "labor", + "ladder", + "lady", + "lake", + "lamp", + "language", + "laptop", + "large", + "later", + "latin", + "laugh", + "laundry", + "lava", + "law", + "lawn", + "lawsuit", + "layer", + "lazy", + "leader", + "leaf", + "learn", + "leave", + "lecture", + "left", + "leg", + "legal", + "legend", + "leisure", + "lemon", + "lend", + "length", + "lens", + "leopard", + "lesson", + "letter", + "level", + "liar", + "liberty", + "library", + "license", + "life", + "lift", + "light", + "like", + "limb", + "limit", + "link", + "lion", + "liquid", + "list", + "little", + "live", + "lizard", + "load", + "loan", + "lobster", + "local", + "lock", + "logic", + "lonely", + "long", + "loop", + "lottery", + "loud", + "lounge", + "love", + "loyal", + "lucky", + "luggage", + "lumber", + "lunar", + "lunch", + "luxury", + "lyrics", + "machine", + "mad", + "magic", + "magnet", + "maid", + "mail", + "main", + "major", + "make", + "mammal", + "man", + "manage", + "mandate", + "mango", + "mansion", + "manual", + "maple", + "marble", + "march", + "margin", + "marine", + "market", + "marriage", + "mask", + "mass", + "master", + "match", + "material", + "math", + "matrix", + "matter", + "maximum", + "maze", + "meadow", + "mean", + "measure", + "meat", + "mechanic", + "medal", + "media", + "melody", + "melt", + "member", + "memory", + "mention", + "menu", + "mercy", + "merge", + "merit", + "merry", + "mesh", + "message", + "metal", + "method", + "middle", + "midnight", + "milk", + "million", + "mimic", + "mind", + "minimum", + "minor", + "minute", + "miracle", + "mirror", + "misery", + "miss", + "mistake", + "mix", + "mixed", + "mixture", + "mobile", + "model", + "modify", + "mom", + "moment", + "monitor", + "monkey", + "monster", + "month", + "moon", + "moral", + "more", + "morning", + "mosquito", + "mother", + "motion", + "motor", + "mountain", + "mouse", + "move", + "movie", + "much", + "muffin", + "mule", + "multiply", + "muscle", + "museum", + "mushroom", + "music", + "must", + "mutual", + "myself", + "mystery", + "myth", + "naive", + "name", + "napkin", + "narrow", + "nasty", + "nation", + "nature", + "near", + "neck", + "need", + "negative", + "neglect", + "neither", + "nephew", + "nerve", + "nest", + "net", + "network", + "neutral", + "never", + "news", + "next", + "nice", + "night", + "noble", + "noise", + "nominee", + "noodle", + "normal", + "north", + "nose", + "notable", + "note", + "nothing", + "notice", + "novel", + "now", + "nuclear", + "number", + "nurse", + "nut", + "oak", + "obey", + "object", + "oblige", + "obscure", + "observe", + "obtain", + "obvious", + "occur", + "ocean", + "october", + "odor", + "off", + "offer", + "office", + "often", + "oil", + "okay", + "old", + "olive", + "olympic", + "omit", + "once", + "one", + "onion", + "online", + "only", + "open", + "opera", + "opinion", + "oppose", + "option", + "orange", + "orbit", + "orchard", + "order", + "ordinary", + "organ", + "orient", + "original", + "orphan", + "ostrich", + "other", + "outdoor", + "outer", + "output", + "outside", + "oval", + "oven", + "over", + "own", + "owner", + "oxygen", + "oyster", + "ozone", + "pact", + "paddle", + "page", + "pair", + "palace", + "palm", + "panda", + "panel", + "panic", + "panther", + "paper", + "parade", + "parent", + "park", + "parrot", + "party", + "pass", + "patch", + "path", + "patient", + "patrol", + "pattern", + "pause", + "pave", + "payment", + "peace", + "peanut", + "pear", + "peasant", + "pelican", + "pen", + "penalty", + "pencil", + "people", + "pepper", + "perfect", + "permit", + "person", + "pet", + "phone", + "photo", + "phrase", + "physical", + "piano", + "picnic", + "picture", + "piece", + "pig", + "pigeon", + "pill", + "pilot", + "pink", + "pioneer", + "pipe", + "pistol", + "pitch", + "pizza", + "place", + "planet", + "plastic", + "plate", + "play", + "please", + "pledge", + "pluck", + "plug", + "plunge", + "poem", + "poet", + "point", + "polar", + "pole", + "police", + "pond", + "pony", + "pool", + "popular", + "portion", + "position", + "possible", + "post", + "potato", + "pottery", + "poverty", + "powder", + "power", + "practice", + "praise", + "predict", + "prefer", + "prepare", + "present", + "pretty", + "prevent", + "price", + "pride", + "primary", + "print", + "priority", + "prison", + "private", + "prize", + "problem", + "process", + "produce", + "profit", + "program", + "project", + "promote", + "proof", + "property", + "prosper", + "protect", + "proud", + "provide", + "public", + "pudding", + "pull", + "pulp", + "pulse", + "pumpkin", + "punch", + "pupil", + "puppy", + "purchase", + "purity", + "purpose", + "purse", + "push", + "put", + "puzzle", + "pyramid", + "quality", + "quantum", + "quarter", + "question", + "quick", + "quit", + "quiz", + "quote", + "rabbit", + "raccoon", + "race", + "rack", + "radar", + "radio", + "rail", + "rain", + "raise", + "rally", + "ramp", + "ranch", + "random", + "range", + "rapid", + "rare", + "rate", + "rather", + "raven", + "raw", + "razor", + "ready", + "real", + "reason", + "rebel", + "rebuild", + "recall", + "receive", + "recipe", + "record", + "recycle", + "reduce", + "reflect", + "reform", + "refuse", + "region", + "regret", + "regular", + "reject", + "relax", + "release", + "relief", + "rely", + "remain", + "remember", + "remind", + "remove", + "render", + "renew", + "rent", + "reopen", + "repair", + "repeat", + "replace", + "report", + "require", + "rescue", + "resemble", + "resist", + "resource", + "response", + "result", + "retire", + "retreat", + "return", + "reunion", + "reveal", + "review", + "reward", + "rhythm", + "rib", + "ribbon", + "rice", + "rich", + "ride", + "ridge", + "rifle", + "right", + "rigid", + "ring", + "riot", + "ripple", + "risk", + "ritual", + "rival", + "river", + "road", + "roast", + "robot", + "robust", + "rocket", + "romance", + "roof", + "rookie", + "room", + "rose", + "rotate", + "rough", + "round", + "route", + "royal", + "rubber", + "rude", + "rug", + "rule", + "run", + "runway", + "rural", + "sad", + "saddle", + "sadness", + "safe", + "sail", + "salad", + "salmon", + "salon", + "salt", + "salute", + "same", + "sample", + "sand", + "satisfy", + "satoshi", + "sauce", + "sausage", + "save", + "say", + "scale", + "scan", + "scare", + "scatter", + "scene", + "scheme", + "school", + "science", + "scissors", + "scorpion", + "scout", + "scrap", + "screen", + "script", + "scrub", + "sea", + "search", + "season", + "seat", + "second", + "secret", + "section", + "security", + "seed", + "seek", + "segment", + "select", + "sell", + "seminar", + "senior", + "sense", + "sentence", + "series", + "service", + "session", + "settle", + "setup", + "seven", + "shadow", + "shaft", + "shallow", + "share", + "shed", + "shell", + "sheriff", + "shield", + "shift", + "shine", + "ship", + "shiver", + "shock", + "shoe", + "shoot", + "shop", + "short", + "shoulder", + "shove", + "shrimp", + "shrug", + "shuffle", + "shy", + "sibling", + "sick", + "side", + "siege", + "sight", + "sign", + "silent", + "silk", + "silly", + "silver", + "similar", + "simple", + "since", + "sing", + "siren", + "sister", + "situate", + "six", + "size", + "skate", + "sketch", + "ski", + "skill", + "skin", + "skirt", + "skull", + "slab", + "slam", + "sleep", + "slender", + "slice", + "slide", + "slight", + "slim", + "slogan", + "slot", + "slow", + "slush", + "small", + "smart", + "smile", + "smoke", + "smooth", + "snack", + "snake", + "snap", + "sniff", + "snow", + "soap", + "soccer", + "social", + "sock", + "soda", + "soft", + "solar", + "soldier", + "solid", + "solution", + "solve", + "someone", + "song", + "soon", + "sorry", + "sort", + "soul", + "sound", + "soup", + "source", + "south", + "space", + "spare", + "spatial", + "spawn", + "speak", + "special", + "speed", + "spell", + "spend", + "sphere", + "spice", + "spider", + "spike", + "spin", + "spirit", + "split", + "spoil", + "sponsor", + "spoon", + "sport", + "spot", + "spray", + "spread", + "spring", + "spy", + "square", + "squeeze", + "squirrel", + "stable", + "stadium", + "staff", + "stage", + "stairs", + "stamp", + "stand", + "start", + "state", + "stay", + "steak", + "steel", + "stem", + "step", + "stereo", + "stick", + "still", + "sting", + "stock", + "stomach", + "stone", + "stool", + "story", + "stove", + "strategy", + "street", + "strike", + "strong", + "struggle", + "student", + "stuff", + "stumble", + "style", + "subject", + "submit", + "subway", + "success", + "such", + "sudden", + "suffer", + "sugar", + "suggest", + "suit", + "summer", + "sun", + "sunny", + "sunset", + "super", + "supply", + "supreme", + "sure", + "surface", + "surge", + "surprise", + "surround", + "survey", + "suspect", + "sustain", + "swallow", + "swamp", + "swap", + "swarm", + "swear", + "sweet", + "swift", + "swim", + "swing", + "switch", + "sword", + "symbol", + "symptom", + "syrup", + "system", + "table", + "tackle", + "tag", + "tail", + "talent", + "talk", + "tank", + "tape", + "target", + "task", + "taste", + "tattoo", + "taxi", + "teach", + "team", + "tell", + "ten", + "tenant", + "tennis", + "tent", + "term", + "test", + "text", + "thank", + "that", + "theme", + "then", + "theory", + "there", + "they", + "thing", + "this", + "thought", + "three", + "thrive", + "throw", + "thumb", + "thunder", + "ticket", + "tide", + "tiger", + "tilt", + "timber", + "time", + "tiny", + "tip", + "tired", + "tissue", + "title", + "toast", + "tobacco", + "today", + "toddler", + "toe", + "together", + "toilet", + "token", + "tomato", + "tomorrow", + "tone", + "tongue", + "tonight", + "tool", + "tooth", + "top", + "topic", + "topple", + "torch", + "tornado", + "tortoise", + "toss", + "total", + "tourist", + "toward", + "tower", + "town", + "toy", + "track", + "trade", + "traffic", + "tragic", + "train", + "transfer", + "trap", + "trash", + "travel", + "tray", + "treat", + "tree", + "trend", + "trial", + "tribe", + "trick", + "trigger", + "trim", + "trip", + "trophy", + "trouble", + "truck", + "true", + "truly", + "trumpet", + "trust", + "truth", + "try", + "tube", + "tuition", + "tumble", + "tuna", + "tunnel", + "turkey", + "turn", + "turtle", + "twelve", + "twenty", + "twice", + "twin", + "twist", + "two", + "type", + "typical", + "ugly", + "umbrella", + "unable", + "unaware", + "uncle", + "uncover", + "under", + "undo", + "unfair", + "unfold", + "unhappy", + "uniform", + "unique", + "unit", + "universe", + "unknown", + "unlock", + "until", + "unusual", + "unveil", + "update", + "upgrade", + "uphold", + "upon", + "upper", + "upset", + "urban", + "urge", + "usage", + "use", + "used", + "useful", + "useless", + "usual", + "utility", + "vacant", + "vacuum", + "vague", + "valid", + "valley", + "valve", + "van", + "vanish", + "vapor", + "various", + "vast", + "vault", + "vehicle", + "velvet", + "vendor", + "venture", + "venue", + "verb", + "verify", + "version", + "very", + "vessel", + "veteran", + "viable", + "vibrant", + "vicious", + "victory", + "video", + "view", + "village", + "vintage", + "violin", + "virtual", + "virus", + "visa", + "visit", + "visual", + "vital", + "vivid", + "vocal", + "voice", + "void", + "volcano", + "volume", + "vote", + "voyage", + "wage", + "wagon", + "wait", + "walk", + "wall", + "walnut", + "want", + "warfare", + "warm", + "warrior", + "wash", + "wasp", + "waste", + "water", + "wave", + "way", + "wealth", + "weapon", + "wear", + "weasel", + "weather", + "web", + "wedding", + "weekend", + "weird", + "welcome", + "west", + "wet", + "whale", + "what", + "wheat", + "wheel", + "when", + "where", + "whip", + "whisper", + "wide", + "width", + "wife", + "wild", + "will", + "win", + "window", + "wine", + "wing", + "wink", + "winner", + "winter", + "wire", + "wisdom", + "wise", + "wish", + "witness", + "wolf", + "woman", + "wonder", + "wood", + "wool", + "word", + "work", + "world", + "worry", + "worth", + "wrap", + "wreck", + "wrestle", + "wrist", + "write", + "wrong", + "yard", + "year", + "yellow", + "you", + "young", + "youth", + "zebra", + "zero", + "zone", + "zoo", +] diff --git a/tonsdk/crypto/exceptions.py b/tonsdk_ng/crypto/exceptions.py similarity index 100% rename from tonsdk/crypto/exceptions.py rename to tonsdk_ng/crypto/exceptions.py diff --git a/tonsdk_ng/provider/__init__.py b/tonsdk_ng/provider/__init__.py new file mode 100644 index 0000000..b380025 --- /dev/null +++ b/tonsdk_ng/provider/__init__.py @@ -0,0 +1,17 @@ +from ._address import address_state, prepare_address +from ._exceptions import ResponseError +from ._toncenter import ToncenterClient, ToncenterWrongResult +from ._tonlibjson import AsyncTonlibClient, SyncTonlibClient, TonLibWrongResult +from ._utils import parse_response + +all = [ + "AsyncTonlibClient", + "SyncTonlibClient", + "ToncenterClient", + "prepare_address", + "address_state", + "parse_response", + "ResponseError", + "TonLibWrongResult", + "ToncenterWrongResult", +] diff --git a/tonsdk/provider/_address.py b/tonsdk_ng/provider/_address.py similarity index 58% rename from tonsdk/provider/_address.py rename to tonsdk_ng/provider/_address.py index dbbfa58..8df35f4 100644 --- a/tonsdk/provider/_address.py +++ b/tonsdk_ng/provider/_address.py @@ -1,11 +1,12 @@ import base64 - -bounceable_tag, non_bounceable_tag = b'\x11', b'\x51' +bounceable_tag, non_bounceable_tag = b"\x11", b"\x51" b64_abc = set( - 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/') + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/" +) b64_abc_urlsafe = set( - 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-') + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-" +) def is_int(x): @@ -27,16 +28,16 @@ def is_hex(x): def calcCRC(message): poly = 0x1021 reg = 0 - message += b'\x00\x00' + message += b"\x00\x00" for byte in message: mask = 0x80 - while(mask > 0): + while mask > 0: reg <<= 1 if byte & mask: reg += 1 mask >>= 1 - if reg > 0xffff: - reg &= 0xffff + if reg > 0xFFFF: + reg &= 0xFFFF reg ^= poly return reg.to_bytes(2, "big") @@ -45,8 +46,7 @@ def account_forms(raw_form, test_only=False): workchain, address = raw_form.split(":") workchain, address = int(workchain), int(address, 16) address = address.to_bytes(32, "big") - workchain_tag = b'\xff' if workchain == - \ - 1 else workchain.to_bytes(1, "big") + workchain_tag = b"\xff" if workchain == -1 else workchain.to_bytes(1, "big") btag = bounceable_tag nbtag = non_bounceable_tag # if test_only: @@ -54,26 +54,30 @@ def account_forms(raw_form, test_only=False): # nbtag = (nbtag[0] | 0x80).to_bytes(1,'big') preaddr_b = btag + workchain_tag + address preaddr_u = nbtag + workchain_tag + address - b64_b = base64.b64encode(preaddr_b+calcCRC(preaddr_b)).decode('utf8') - b64_u = base64.b64encode(preaddr_u+calcCRC(preaddr_u)).decode('utf8') - b64_b_us = base64.urlsafe_b64encode( - preaddr_b+calcCRC(preaddr_b)).decode('utf8') - b64_u_us = base64.urlsafe_b64encode( - preaddr_u+calcCRC(preaddr_u)).decode('utf8') - return {'raw_form': raw_form, - 'bounceable': {'b64': b64_b, 'b64url': b64_b_us}, - 'non_bounceable': {'b64': b64_u, 'b64url': b64_u_us}, - 'given_type': 'raw_form', - 'test_only': test_only} + b64_b = base64.b64encode(preaddr_b + calcCRC(preaddr_b)).decode("utf8") + b64_u = base64.b64encode(preaddr_u + calcCRC(preaddr_u)).decode("utf8") + b64_b_us = base64.urlsafe_b64encode(preaddr_b + calcCRC(preaddr_b)).decode( + "utf8" + ) + b64_u_us = base64.urlsafe_b64encode(preaddr_u + calcCRC(preaddr_u)).decode( + "utf8" + ) + return { + "raw_form": raw_form, + "bounceable": {"b64": b64_b, "b64url": b64_b_us}, + "non_bounceable": {"b64": b64_u, "b64url": b64_u_us}, + "given_type": "raw_form", + "test_only": test_only, + } def read_friendly_address(address): urlsafe = False if set(address).issubset(b64_abc): - address_bytes = base64.b64decode(address.encode('utf8')) + address_bytes = base64.b64decode(address.encode("utf8")) elif set(address).issubset(b64_abc_urlsafe): urlsafe = True - address_bytes = base64.urlsafe_b64decode(address.encode('utf8')) + address_bytes = base64.urlsafe_b64decode(address.encode("utf8")) else: raise Exception("Not an address") if not calcCRC(address_bytes[:-2]) == address_bytes[-2:]: @@ -84,30 +88,35 @@ def read_friendly_address(address): tag = tag ^ 0x80 else: test_only = False - tag = tag.to_bytes(1, 'big') + tag = tag.to_bytes(1, "big") if tag == bounceable_tag: bounceable = True elif tag == non_bounceable_tag: bounceable = False else: raise Exception("Unknown tag") - if address_bytes[1:2] == b'\xff': + if address_bytes[1:2] == b"\xff": workchain = -1 else: workchain = address_bytes[1] hx = hex(int.from_bytes(address_bytes[2:-2], "big"))[2:] - hx = (64-len(hx))*"0"+hx - raw_form = str(workchain)+":"+hx + hx = (64 - len(hx)) * "0" + hx + raw_form = str(workchain) + ":" + hx account = account_forms(raw_form, test_only) - account['given_type'] = "friendly_" + \ - ("bounceable" if bounceable else "non_bounceable") + account["given_type"] = "friendly_" + ( + "bounceable" if bounceable else "non_bounceable" + ) return account def detect_address(unknown_form): if is_hex(unknown_form): - return account_forms("-1:"+unknown_form) - elif (":" in unknown_form) and is_int(unknown_form.split(":")[0]) and is_hex(unknown_form.split(":")[1]): + return account_forms("-1:" + unknown_form) + elif ( + (":" in unknown_form) + and is_int(unknown_form.split(":")[0]) + and is_hex(unknown_form.split(":")[1]) + ): return account_forms(unknown_form) else: return read_friendly_address(unknown_form) @@ -115,13 +124,16 @@ def detect_address(unknown_form): def prepare_address(unknown_form): address = detect_address(unknown_form) - if 'non_bounceable' in address['given_type']: + if "non_bounceable" in address["given_type"]: return address["non_bounceable"]["b64"] return address["bounceable"]["b64"] def address_state(account_info): - if isinstance(account_info.get("code", ""), int) or len(account_info.get("code", "")) == 0: + if ( + isinstance(account_info.get("code", ""), int) + or len(account_info.get("code", "")) == 0 + ): if len(account_info.get("frozen_hash", "")) == 0: return "uninitialized" else: diff --git a/tonsdk/provider/_exceptions.py b/tonsdk_ng/provider/_exceptions.py similarity index 98% rename from tonsdk/provider/_exceptions.py rename to tonsdk_ng/provider/_exceptions.py index 2e5d618..de1d98e 100644 --- a/tonsdk/provider/_exceptions.py +++ b/tonsdk_ng/provider/_exceptions.py @@ -1,5 +1,3 @@ - - class ResponseError(Exception): def __init__(self, message, exit_code): super().__init__(message) diff --git a/tonsdk/provider/_toncenter/__init__.py b/tonsdk_ng/provider/_toncenter/__init__.py similarity index 57% rename from tonsdk/provider/_toncenter/__init__.py rename to tonsdk_ng/provider/_toncenter/__init__.py index fc0eff5..8299863 100644 --- a/tonsdk/provider/_toncenter/__init__.py +++ b/tonsdk_ng/provider/_toncenter/__init__.py @@ -1,6 +1,6 @@ from ._client import ToncenterClient, ToncenterWrongResult __all__ = [ - 'ToncenterClient', - 'ToncenterWrongResult', -] \ No newline at end of file + "ToncenterClient", + "ToncenterWrongResult", +] diff --git a/tonsdk/provider/_toncenter/_client.py b/tonsdk_ng/provider/_toncenter/_client.py similarity index 58% rename from tonsdk/provider/_toncenter/_client.py rename to tonsdk_ng/provider/_toncenter/_client.py index d74373f..487f33a 100644 --- a/tonsdk/provider/_toncenter/_client.py +++ b/tonsdk_ng/provider/_toncenter/_client.py @@ -14,34 +14,50 @@ def __init__(self, base_url: str, api_key: Optional[str]): self.api_key = api_key def raw_send_message(self, serialized_boc): - serialized_boc = codecs.decode(codecs.encode( - serialized_boc, "base64"), 'utf-8').replace("\n", '') + serialized_boc = codecs.decode( + codecs.encode(serialized_boc, "base64"), "utf-8" + ).replace("\n", "") return { "func": self.__post_request, "args": [self.base_url + "sendBoc"], - "kwargs": {"data": {"boc": serialized_boc}} + "kwargs": {"data": {"boc": serialized_boc}}, } def raw_run_method(self, address, method, stack_data, output_layout=None): return { "func": self.__post_request, "args": [self.base_url + "runGetMethod"], - "kwargs": {"data": {"address": address, "method": method, "stack": stack_data}} + "kwargs": { + "data": { + "address": address, + "method": method, + "stack": stack_data, + } + }, } def raw_get_account_state(self, prepared_address: str): return { "func": self.__jsonrpc_request, "args": ["getAddressInformation"], - "kwargs": {"params": {"address": prepared_address}} + "kwargs": {"params": {"address": prepared_address}}, } async def __post_request(self, session, url, data): - async with session.post(url, data=json.dumps(data), headers=self.__headers()) as resp: + async with session.post( + url, data=json.dumps(data), headers=self.__headers() + ) as resp: return await self.__parse_response(resp) - async def __jsonrpc_request(self, session, method: str, params: Dict, id: str = "1", jsonrpc: str = "2.0"): + async def __jsonrpc_request( + self, + session, + method: str, + params: Dict, + id: str = "1", + jsonrpc: str = "2.0", + ): payload = { "id": id, "jsonrpc": jsonrpc, @@ -49,13 +65,15 @@ async def __jsonrpc_request(self, session, method: str, params: Dict, id: str = "params": params, } - async with session.post(self.base_url + "jsonRPC", json=payload, headers=self.__headers()) as resp: + async with session.post( + self.base_url + "jsonRPC", json=payload, headers=self.__headers() + ) as resp: return await self.__parse_response(resp) def __headers(self): headers = { - 'Content-Type': 'application/json', - 'accept': 'application/json', + "Content-Type": "application/json", + "accept": "application/json", } if self.api_key: headers["X-API-Key"] = self.api_key @@ -68,7 +86,7 @@ async def __parse_response(self, resp): except Exception: # TODO: catch correct exceptions raise ToncenterWrongResult(resp.status) - if not resp['ok']: - raise ToncenterWrongResult(resp['code']) + if not resp["ok"]: + raise ToncenterWrongResult(resp["code"]) - return resp['result'] + return resp["result"] diff --git a/tonsdk/provider/_tonlibjson/__init__.py b/tonsdk_ng/provider/_tonlibjson/__init__.py similarity index 57% rename from tonsdk/provider/_tonlibjson/__init__.py rename to tonsdk_ng/provider/_tonlibjson/__init__.py index febf8a4..a5ec9ba 100644 --- a/tonsdk/provider/_tonlibjson/__init__.py +++ b/tonsdk_ng/provider/_tonlibjson/__init__.py @@ -2,8 +2,4 @@ from ._sync import SyncTonlibClient from ._utils import TonLibWrongResult -all = [ - 'AsyncTonlibClient', - 'SyncTonlibClient', - 'TonLibWrongResult' -] +all = ["AsyncTonlibClient", "SyncTonlibClient", "TonLibWrongResult"] diff --git a/tonsdk/provider/_tonlibjson/_async/__init__.py b/tonsdk_ng/provider/_tonlibjson/_async/__init__.py similarity index 62% rename from tonsdk/provider/_tonlibjson/_async/__init__.py rename to tonsdk_ng/provider/_tonlibjson/_async/__init__.py index 06a866a..6c9318d 100644 --- a/tonsdk/provider/_tonlibjson/_async/__init__.py +++ b/tonsdk_ng/provider/_tonlibjson/_async/__init__.py @@ -2,6 +2,6 @@ from ._wrapper import AsyncTonLibJsonWrapper __all__ = [ - 'AsyncTonlibClient', - 'AsyncTonLibJsonWrapper', -] \ No newline at end of file + "AsyncTonlibClient", + "AsyncTonLibJsonWrapper", +] diff --git a/tonsdk_ng/provider/_tonlibjson/_async/_client.py b/tonsdk_ng/provider/_tonlibjson/_async/_client.py new file mode 100644 index 0000000..e5da3dd --- /dev/null +++ b/tonsdk_ng/provider/_tonlibjson/_async/_client.py @@ -0,0 +1,868 @@ +import asyncio +import codecs +import json +import logging +import random +from copy import deepcopy +from pathlib import Path + +from tvm_valuetypes import deserialize_boc, render_tvm_stack + +from ..._address import detect_address, prepare_address +from .._utils import ( + CtypesStdoutCapture, + TonLibWrongResult, + b64str_to_hex, + hash_to_hex, + hex_to_b64str, +) +from ._wrapper import AsyncTonLibJsonWrapper + +logger = logging.getLogger(__name__) + + +class AsyncTonlibClient: + def __init__( + self, config, keystore, loop, cdll_path=None, verbosity_level=0 + ): + self.ls_index = random.randrange(0, len(config["liteservers"])) + self.config = config + self.keystore = keystore + self.cdll_path = cdll_path + self.loop = loop + self.verbosity_level = verbosity_level + self.max_parallel_requests = config["liteservers"][0].get( + "max_parallel_requests", 50 + ) + + self.semaphore = None + self.tonlib_wrapper = None + self.loaded_contracts_num = None + + @property + def local_config(self): + local = deepcopy(self.config) + local["liteservers"] = [local["liteservers"][self.ls_index]] + return local + + async def reconnect(self, max_restarts=None): + if max_restarts is not None: + max_restarts -= 1 + if max_restarts is None or max_restarts >= 0: + await self.init(max_restarts) + logger.info( + f"Client #{self.ls_index:03d} reconnected (max_restarts: {max_restarts})" + ) + else: + logger.info( + "Client #{self.ls_index:03d} has no reconnect attempts left" + ) + self.tonlib_wrapper = None + + async def init(self, max_restarts=None): + """ + TL Spec + init options:options = options.Info; + options config:config keystore_type:KeyStoreType = Options; + + keyStoreTypeDirectory directory:string = KeyStoreType; + config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config; + + :param ip: IPv4 address in dotted notation or signed int32 + :param port: IPv4 TCP port + :param key: base64 pub key of liteserver node + :return: None + """ + self.semaphore = asyncio.Semaphore(self.max_parallel_requests) + + self.loaded_contracts_num = 0 + wrapper = AsyncTonLibJsonWrapper( + self.loop, self.ls_index, self.cdll_path + ) + keystore_obj = { + "@type": "keyStoreTypeDirectory", + "directory": self.keystore, + } + # create keystore + Path(self.keystore).mkdir(parents=True, exist_ok=True) + + request = { + "@type": "init", + "options": { + "@type": "options", + "config": { + "@type": "config", + "config": json.dumps(self.local_config), + "use_callbacks_for_network": False, + "blockchain_name": "", + "ignore_cache": False, + }, + "keystore_type": keystore_obj, + }, + } + self.tonlib_wrapper = wrapper + with CtypesStdoutCapture(): + # set verbosity level + await self.set_verbosity_level(self.verbosity_level) + + # set confog + init_result = await self.tonlib_wrapper.execute(request) + self.tonlib_wrapper.set_restart_hook( + hook=self.reconnect, max_requests=1024, max_restarts=max_restarts + ) + + logger.info(f"TonLib #{self.ls_index:03d} inited successfully") + + return init_result + + async def set_verbosity_level(self, level): + request = { + "@type": "setLogVerbosityLevel", + "new_verbosity_level": level, + } + return await self.tonlib_wrapper.execute(request) + + async def raw_get_transactions( + self, + account_address: str, + from_transaction_lt: str, + from_transaction_hash: str, + ): + """ + TL Spec: + raw.getTransactions account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions; + accountAddress account_address:string = AccountAddress; + internal.transactionId lt:int64 hash:bytes = internal.TransactionId; + :param account_address: str with raw or user friendly address + :param from_transaction_lt: from transaction lt + :param from_transaction_hash: from transaction hash in HEX representation + :return: dict as + { + '@type': 'raw.transactions', + 'transactions': list[dict as { + '@type': 'raw.transaction', + 'utime': int, + 'data': str, + 'transaction_id': internal.transactionId, + 'fee': str, + 'in_msg': dict as { + '@type': 'raw.message', + 'source': str, + 'destination': str, + 'value': str, + 'message': str + }, + 'out_msgs': list[dict as raw.message] + }], + 'previous_transaction_id': internal.transactionId + } + """ + account_address = prepare_address(account_address) + from_transaction_hash = hex_to_b64str(from_transaction_hash) + + request = { + "@type": "raw.getTransactions", + "account_address": { + "account_address": account_address, + }, + "from_transaction_id": { + "@type": "internal.transactionId", + "lt": from_transaction_lt, + "hash": from_transaction_hash, + }, + } + return await self.tonlib_wrapper.execute(request) + + async def raw_get_account_state(self, address: str): + """ + TL Spec: + raw.getAccountState account_address:accountAddress = raw.AccountState; + accountAddress account_address:string = AccountAddress; + :param address: str with raw or user friendly address + :return: dict as + { + '@type': 'raw.accountState', + 'balance': str, + 'code': str, + 'data': str, + 'last_transaction_id': internal.transactionId, + 'sync_utime': int + } + """ + account_address = prepare_address( + address + ) # TODO: understand why this is not used + request = { + "@type": "raw.getAccountState", + "account_address": {"account_address": address}, + } + + return await self.tonlib_wrapper.execute(request) + + async def generic_get_account_state(self, address: str): + # TODO: understand why this is not used + account_address = prepare_address(address) + request = { + "@type": "getAccountState", + "account_address": {"account_address": address}, + } + return await self.tonlib_wrapper.execute(request) + + async def _load_contract(self, address): + # TODO: understand why this is not used + account_address = prepare_address(address) + request = { + "@type": "smc.load", + "account_address": {"account_address": address}, + } + result = await self.tonlib_wrapper.execute(request) + if result.get("@type", "error") == "error": + raise TonLibWrongResult("smc.load failed", result) + self.loaded_contracts_num += 1 + return result["id"] + + async def raw_run_method( + self, address, method, stack_data, output_layout=None + ): + """ + For numeric data only + TL Spec: + smc.runGetMethod id:int53 method:smc.MethodId stack:vector = smc.RunResult; + + smc.methodIdNumber number:int32 = smc.MethodId; + smc.methodIdName name:string = smc.MethodId; + + tvm.slice bytes:string = tvm.Slice; + tvm.cell bytes:string = tvm.Cell; + tvm.numberDecimal number:string = tvm.Number; + tvm.tuple elements:vector = tvm.Tuple; + tvm.list elements:vector = tvm.List; + + tvm.stackEntrySlice slice:tvm.slice = tvm.StackEntry; + tvm.stackEntryCell cell:tvm.cell = tvm.StackEntry; + tvm.stackEntryNumber number:tvm.Number = tvm.StackEntry; + tvm.stackEntryTuple tuple:tvm.Tuple = tvm.StackEntry; + tvm.stackEntryList list:tvm.List = tvm.StackEntry; + tvm.stackEntryUnsupported = tvm.StackEntry; + + smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; + """ + stack_data = render_tvm_stack(stack_data) + if isinstance(method, int): + method = {"@type": "smc.methodIdNumber", "number": method} + else: + method = {"@type": "smc.methodIdName", "name": str(method)} + contract_id = await self._load_contract(address) + request = { + "@type": "smc.runGetMethod", + "id": contract_id, + "method": method, + "stack": stack_data, + } + + return await self.tonlib_wrapper.execute(request) + + async def raw_send_message(self, serialized_boc): + """ + raw.sendMessage body:bytes = Ok; + + :param serialized_boc: bytes, serialized bag of cell + """ + serialized_boc = codecs.decode( + codecs.encode(serialized_boc, "base64"), "utf-8" + ).replace("\n", "") + request = {"@type": "raw.sendMessage", "body": serialized_boc} + return await self.tonlib_wrapper.execute(request) + + async def _raw_create_query( + self, destination, body, init_code=b"", init_data=b"" + ): + """ + raw.createQuery destination:accountAddress init_code:bytes init_data:bytes body:bytes = query.Info; + + query.info id:int53 valid_until:int53 body_hash:bytes = query.Info; + + """ + init_code = codecs.decode( + codecs.encode(init_code, "base64"), "utf-8" + ).replace("\n", "") + init_data = codecs.decode( + codecs.encode(init_data, "base64"), "utf-8" + ).replace("\n", "") + body = codecs.decode(codecs.encode(body, "base64"), "utf-8").replace( + "\n", "" + ) + destination = prepare_address(destination) + request = { + "@type": "raw.createQuery", + "body": body, + "init_code": init_code, + "init_data": init_data, + "destination": {"account_address": destination}, + } + result = await self.tonlib_wrapper.execute(request) + if result.get("@type", "error") == "error": + raise TonLibWrongResult("raw.createQuery failed", result) + return result + + async def _raw_send_query(self, query_info): + """ + query.send id:int53 = Ok; + """ + request = {"@type": "query.send", "id": query_info["id"]} + return await self.tonlib_wrapper.execute(request) + + async def raw_create_and_send_query( + self, destination, body, init_code=b"", init_data=b"" + ): + query_info = await self._raw_create_query( + destination, body, init_code, init_data + ) + return self._raw_send_query(query_info) + + async def raw_create_and_send_message( + self, destination, body, initial_account_state=b"" + ): + # Very close to raw_create_and_send_query, but StateInit should be generated outside + """ + raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok; + + """ + initial_account_state = codecs.decode( + codecs.encode(initial_account_state, "base64"), "utf-8" + ).replace("\n", "") + body = codecs.decode(codecs.encode(body, "base64"), "utf-8").replace( + "\n", "" + ) + destination = prepare_address(destination) + request = { + "@type": "raw.createAndSendMessage", + "destination": {"account_address": destination}, + "initial_account_state": initial_account_state, + "data": body, + } + return await self.tonlib_wrapper.execute(request) + + async def raw_estimate_fees( + self, + destination, + body, + init_code=b"", + init_data=b"", + ignore_chksig=True, + ): + query_info = await self._raw_create_query( + destination, body, init_code, init_data + ) + request = { + "@type": "query.estimateFees", + "id": query_info["id"], + "ignore_chksig": ignore_chksig, + } + return await self.tonlib_wrapper.execute(request) + + async def raw_get_block_transactions(self, fullblock, count, after_tx): + request = { + "@type": "blocks.getTransactions", + "id": fullblock, + "mode": 7 if not after_tx else 7 + 128, + "count": count, + "after": after_tx, + } + return await self.tonlib_wrapper.execute(request) + + async def raw_get_block_transactions_ext(self, fullblock, count, after_tx): + request = { + "@type": "blocks.getTransactionsExt", + "id": fullblock, + "mode": 7 if not after_tx else 7 + 128, + "count": count, + "after": after_tx, + } + return await self.tonlib_wrapper.execute(request) + + async def get_transactions( + self, + account, + from_transaction_lt=None, + from_transaction_hash=None, + to_transaction_lt=0, + limit=10, + decode_messages=True, + *args, + **kwargs, + ): + """ + Return all transactions between from_transaction_lt and to_transaction_lt + if to_transaction_lt and to_transaction_hash are not defined returns all transactions + if from_transaction_lt and from_transaction_hash are not defined checks last + """ + if from_transaction_hash: + from_transaction_hash = hash_to_hex(from_transaction_hash) + if (from_transaction_lt == None) or (from_transaction_hash == None): + addr = await self.raw_get_account_state(account) + if "@type" in addr and addr["@type"] == "error": + addr = await self.raw_get_account_state(account) + if "@type" in addr and addr["@type"] == "error": + raise TonLibWrongResult("raw.getAccountState failed", addr) + try: + from_transaction_lt, from_transaction_hash = int( + addr["last_transaction_id"]["lt"] + ), b64str_to_hex(addr["last_transaction_id"]["hash"]) + except KeyError: + raise TonLibWrongResult( + "Can't get last_transaction_id data", addr + ) + reach_lt = False + all_transactions = [] + current_lt, curret_hash = from_transaction_lt, from_transaction_hash + while (not reach_lt) and (len(all_transactions) < limit): + raw_transactions = await self.raw_get_transactions( + account, current_lt, curret_hash + ) + if (raw_transactions["@type"]) == "error": + break + # TODO probably we should chenge get_transactions API + # if 'message' in raw_transactions['message']: + # raise Exception(raw_transactions['message']) + # else: + # raise Exception("Can't get transactions") + transactions, next = raw_transactions[ + "transactions" + ], raw_transactions.get("previous_transaction_id", None) + for t in transactions: + tlt = int(t["transaction_id"]["lt"]) + if tlt <= to_transaction_lt: + reach_lt = True + break + all_transactions.append(t) + if next: + current_lt, curret_hash = int(next["lt"]), b64str_to_hex( + next["hash"] + ) + else: + break + if current_lt == 0: + break + + all_transactions = all_transactions[:limit] + for t in all_transactions: + try: + if "in_msg" in t: + if "source" in t["in_msg"]: + t["in_msg"]["source"] = t["in_msg"]["source"][ + "account_address" + ] + if "destination" in t["in_msg"]: + t["in_msg"]["destination"] = t["in_msg"]["destination"][ + "account_address" + ] + if decode_messages: + try: + if "msg_data" in t["in_msg"]: + dcd = "" + if ( + t["in_msg"]["msg_data"]["@type"] + == "msg.dataRaw" + ): + msg_cell_boc = codecs.decode( + codecs.encode( + t["in_msg"]["msg_data"]["body"], + "utf8", + ), + "base64", + ) + message_cell = deserialize_boc(msg_cell_boc) + dcd = message_cell.data.data.tobytes() + t["in_msg"]["message"] = codecs.decode( + codecs.encode(dcd, "base64"), "utf8" + ) + elif ( + t["in_msg"]["msg_data"]["@type"] + == "msg.dataText" + ): + dcd = codecs.encode( + t["in_msg"]["msg_data"]["text"], "utf8" + ) + t["in_msg"]["message"] = codecs.decode( + codecs.decode(dcd, "base64"), "utf8" + ) + except Exception as e: + t["in_msg"]["message"] = "" + logger.warning( + f"in_msg message decoding exception: {e}" + ) + if "out_msgs" in t: + for o in t["out_msgs"]: + if "source" in o: + o["source"] = o["source"]["account_address"] + if "destination" in o: + o["destination"] = o["destination"][ + "account_address" + ] + if decode_messages: + try: + if "msg_data" in o: + dcd = "" + if o["msg_data"]["@type"] == "msg.dataRaw": + msg_cell_boc = codecs.decode( + codecs.encode( + o["msg_data"]["body"], "utf8" + ), + "base64", + ) + message_cell = deserialize_boc( + msg_cell_boc + ) + dcd = message_cell.data.data.tobytes() + o["message"] = codecs.decode( + codecs.encode(dcd, "base64"), "utf8" + ) + elif ( + o["msg_data"]["@type"] == "msg.dataText" + ): + dcd = codecs.encode( + o["msg_data"]["text"], "utf8" + ) + o["message"] = codecs.decode( + codecs.decode(dcd, "base64"), "utf8" + ) + except Exception as e: + o["message"] = "" + logger.warning( + f"out_msg message decoding exception: {e}" + ) + except Exception as e: + logger.error(f"getTransaction exception: {e}") + return all_transactions + + async def get_masterchain_info(self, *args, **kwargs): + request = {"@type": "blocks.getMasterchainInfo"} + result = await self.tonlib_wrapper.execute(request) + if result.get("@type", "error") == "error": + raise TonLibWrongResult("blocks.getMasterchainInfo failed", result) + return result + + async def lookup_block( + self, + workchain, + shard, + seqno=None, + lt=None, + unixtime=None, + *args, + **kwargs, + ): + assert ( + seqno or lt or unixtime + ), "Seqno, LT or unixtime should be defined" + mode = 0 + if seqno: + mode += 1 + if lt: + mode += 2 + if unixtime: + mode += 4 + request = { + "@type": "blocks.lookupBlock", + "mode": mode, + "id": { + "@type": "ton.blockId", + "workchain": workchain, + "shard": shard, + "seqno": seqno, + }, + "lt": lt, + "utime": unixtime, + } + return await self.tonlib_wrapper.execute(request) + + async def get_shards( + self, master_seqno=None, lt=None, unixtime=None, *args, **kwargs + ): + assert ( + master_seqno or lt or unixtime + ), "Seqno, LT or unixtime should be defined" + wc, shard = -1, -9223372036854775808 + fullblock = await self.lookup_block( + wc, shard, master_seqno, lt, unixtime + ) + request = {"@type": "blocks.getShards", "id": fullblock} + return await self.tonlib_wrapper.execute(request) + + async def get_block_transactions( + self, + workchain, + shard, + seqno, + count, + root_hash=None, + file_hash=None, + after_lt=None, + after_hash=None, + *args, + **kwargs, + ): + if root_hash and file_hash: + fullblock = { + "@type": "ton.blockIdExt", + "workchain": workchain, + "shard": shard, + "seqno": seqno, + "root_hash": root_hash, + "file_hash": file_hash, + } + else: + fullblock = await self.lookup_block(workchain, shard, seqno) + if fullblock.get("@type", "error") == "error": + return fullblock + after_tx = { + "@type": "blocks.accountTransactionId", + "account": ( + after_hash + if after_hash + else "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + ), + "lt": after_lt if after_lt else 0, + } + total_result = {} + incomplete = True + + while incomplete: + result = await self.raw_get_block_transactions( + fullblock, count, after_tx + ) + if (result["@type"]) == "error": + result = await self.raw_get_block_transactions( + fullblock, count, after_tx + ) + if (result["@type"]) == "error": + raise TonLibWrongResult("Can't get blockTransactions", result) + if not total_result: + total_result = result + else: + total_result["transactions"] += result["transactions"] + total_result["incomplete"] = result["incomplete"] + incomplete = result["incomplete"] + if incomplete: + after_tx["account"] = result["transactions"][-1]["account"] + after_tx["lt"] = result["transactions"][-1]["lt"] + + # TODO automatically check incompleteness and download all txes + for tx in total_result["transactions"]: + try: + tx["account"] = "%d:%s" % ( + result["id"]["workchain"], + b64str_to_hex(tx["account"]), + ) + except: + pass + return total_result + + async def get_block_transactions_ext( + self, + workchain, + shard, + seqno, + count, + root_hash=None, + file_hash=None, + after_lt=None, + after_hash=None, + *args, + **kwargs, + ): + if root_hash and file_hash: + fullblock = { + "@type": "ton.blockIdExt", + "workchain": workchain, + "shard": shard, + "seqno": seqno, + "root_hash": root_hash, + "file_hash": file_hash, + } + else: + fullblock = await self.lookup_block(workchain, shard, seqno) + if fullblock.get("@type", "error") == "error": + return fullblock + after_tx = { + "@type": "blocks.accountTransactionId", + "account": ( + after_hash + if after_hash + else "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + ), + "lt": after_lt if after_lt else 0, + } + total_result = {} + incomplete = True + + while incomplete: + result = await self.raw_get_block_transactions_ext( + fullblock, count, after_tx + ) + if (result["@type"]) == "error": + result = await self.raw_get_block_transactions_ext( + fullblock, count, after_tx + ) + if (result["@type"]) == "error": + raise TonLibWrongResult("Can't get blockTransactions", result) + if not total_result: + total_result = result + else: + total_result["transactions"] += result["transactions"] + total_result["incomplete"] = result["incomplete"] + incomplete = result["incomplete"] + if incomplete: + account_friendly = result["transactions"][-1]["address"][ + "account_address" + ] + hex_without_workchain = detect_address(account_friendly)[ + "raw_form" + ].split(":")[1] + after = hex_to_b64str(hex_without_workchain) + after_tx["account"] = after + after_tx["lt"] = result["transactions"][-1]["transaction_id"][ + "lt" + ] + + for tx in total_result["transactions"]: + try: + account_friendly = tx["address"]["account_address"] + hex_without_workchain = detect_address(account_friendly)[ + "raw_form" + ].split(":")[1] + tx["account"] = "%d:%s" % ( + result["id"]["workchain"], + hex_without_workchain, + ) + except: + pass + return total_result + + async def get_block_header( + self, + workchain, + shard, + seqno, + root_hash=None, + file_hash=None, + *args, + **kwargs, + ): + if root_hash and file_hash: + fullblock = { + "@type": "ton.blockIdExt", + "workchain": workchain, + "shard": shard, + "seqno": seqno, + "root_hash": root_hash, + "file_hash": file_hash, + } + else: + fullblock = await self.lookup_block(workchain, shard, seqno) + if fullblock.get("@type", "error") == "error": + return fullblock + request = {"@type": "blocks.getBlockHeader", "id": fullblock} + return await self.tonlib_wrapper.execute(request) + + async def try_locate_tx_by_incoming_message( + self, source, destination, creation_lt, *args, **kwargs + ): + src = detect_address(source) + dest = detect_address(destination) + workchain = dest["raw_form"].split(":")[0] + shards = await self.get_shards(lt=int(creation_lt)) + + for shard_data in shards["shards"]: + shardchain = shard_data["shard"] + for b in range(3): + block = await self.lookup_block( + workchain, shardchain, lt=int(creation_lt) + b * 1000000 + ) + txs = await self.get_block_transactions( + workchain, + shardchain, + block["seqno"], + count=40, + root_hash=block["root_hash"], + file_hash=block["file_hash"], + ) + candidate = tuple() + count = 0 + for tx in txs["transactions"]: + if tx["account"] == dest["raw_form"]: + count += 1 + if not candidate or candidate[1] < int(tx["lt"]): + candidate = tx["hash"], int(tx["lt"]) + if candidate: + txses = await self.get_transactions( + destination, + from_transaction_lt=candidate[1], + from_transaction_hash=b64str_to_hex(candidate[0]), + limit=max(count, 10), + ) + for tx in txses: + try: + in_msg = tx["in_msg"] + tx_source = in_msg["source"] + if ( + len(tx_source) + and detect_address(tx_source)["raw_form"] + == src["raw_form"] + ): + if int(in_msg["created_lt"]) == int( + creation_lt + ): + return tx + except KeyError: + pass + raise Exception("Tx not found") + + async def try_locate_tx_by_outcoming_message( + self, source, destination, creation_lt, *args, **kwargs + ): + src = detect_address(source) + dest = detect_address(destination) + workchain = src["raw_form"].split(":")[0] + shards = await self.get_shards(lt=int(creation_lt)) + + for shard_data in shards["shards"]: + shardchain = shard_data["shard"] + block = await self.lookup_block( + workchain, shardchain, lt=int(creation_lt) + ) + txses = await self.get_block_transactions( + workchain, + shardchain, + block["seqno"], + count=40, + root_hash=block["root_hash"], + file_hash=block["file_hash"], + ) + candidate = tuple() + count = 0 + for tx in txses["transactions"]: + if tx["account"] == src["raw_form"]: + count += 1 + if not candidate or candidate[1] < int(tx["lt"]): + candidate = tx["hash"], int(tx["lt"]) + if candidate: + txses = await self.get_transactions( + source, + from_transaction_lt=candidate[1], + from_transaction_hash=b64str_to_hex(candidate[0]), + limit=max(count, 10), + ) + for tx in txses: + try: + for msg in tx["out_msgs"]: + if ( + detect_address(msg["destination"])["raw_form"] + == dest["raw_form"] + ): + if int(msg["created_lt"]) == int(creation_lt): + return tx + except KeyError: + pass + raise Exception("Tx not found") diff --git a/tonsdk/provider/_tonlibjson/_async/_wrapper.py b/tonsdk_ng/provider/_tonlibjson/_async/_wrapper.py similarity index 79% rename from tonsdk/provider/_tonlibjson/_async/_wrapper.py rename to tonsdk_ng/provider/_tonlibjson/_async/_wrapper.py index 6bff56e..b522dd9 100644 --- a/tonsdk/provider/_tonlibjson/_async/_wrapper.py +++ b/tonsdk_ng/provider/_tonlibjson/_async/_wrapper.py @@ -5,7 +5,7 @@ import random import time import traceback -from ctypes import CDLL, c_void_p, c_char_p, c_double +from ctypes import CDLL, c_char_p, c_double, c_void_p from .._utils import get_tonlib_cdll_path @@ -50,9 +50,11 @@ def __init__(self, loop, ls_index, cdll_path=None, verbose=0): self.loop = loop self.ls_index = ls_index self.read_results_task = asyncio.ensure_future( - self.read_results(), loop=self.loop) + self.read_results(), loop=self.loop + ) self.del_expired_futures_task = asyncio.ensure_future( - self.del_expired_futures_loop(), loop=self.loop) + self.del_expired_futures_loop(), loop=self.loop + ) self.shutdown_state = False # False, "started", "finished" self.request_num = 0 self.verbose = verbose @@ -64,11 +66,11 @@ def __del__(self): try: self._tonlib_json_client_destroy(self._client) except Exception: - logger.error(f'Traceback: {traceback.format_exc()}') + logger.error(f"Traceback: {traceback.format_exc()}") asyncio.ensure_future(self.restart(), loop=self.loop) def send(self, query): - query = json.dumps(query).encode('utf-8') + query = json.dumps(query).encode("utf-8") try: self._tonlib_json_client_send(self._client, query) except Exception: @@ -77,18 +79,20 @@ def send(self, query): async def restart(self): if not self.shutdown_state: self.shutdown_state = "started" - asyncio.ensure_future(self.restart_hook( - self.max_restarts), loop=self.loop) + asyncio.ensure_future( + self.restart_hook(self.max_restarts), loop=self.loop + ) def receive(self, timeout=10): result = None try: result = self._tonlib_json_client_receive( - self._client, timeout) # time.sleep # asyncio.sleep + self._client, timeout + ) # time.sleep # asyncio.sleep except Exception: asyncio.ensure_future(self.restart(), loop=self.loop) if result: - result = json.loads(result.decode('utf-8')) + result = json.loads(result.decode("utf-8")) return result def set_restart_hook(self, hook, max_requests=None, max_restarts=None): @@ -97,10 +101,13 @@ def set_restart_hook(self, hook, max_requests=None, max_restarts=None): self.restart_hook = hook def execute(self, query, timeout=10): - query_type = query.get('@type', '?') + query_type = query.get("@type", "?") extra_id = "%s:%s:%s" % ( - time.time()+timeout, self.ls_index, random.random()) + time.time() + timeout, + self.ls_index, + random.random(), + ) query["@extra"] = extra_id self.loop.run_in_executor(None, lambda: self.send(query)) @@ -117,7 +124,9 @@ def execute(self, query, timeout=10): @property def _is_finishing(self): - return (not len(self.futures)) and (self.shutdown_state in ["started", "finished"]) + return (not len(self.futures)) and ( + self.shutdown_state in ["started", "finished"] + ) async def read_results(self): timeout = 3 @@ -127,25 +136,34 @@ async def read_results(self): while not self._is_finishing: result = None try: - result = await asyncio.wait_for(self.loop.run_in_executor(None, receive_func), timeout=timeout + delta) + result = await asyncio.wait_for( + self.loop.run_in_executor(None, receive_func), + timeout=timeout + delta, + ) except asyncio.TimeoutError: logger.critical(f"Tonlib #{self.ls_index:03d} Stuck!") asyncio.ensure_future(self.restart(), loop=self.loop) await asyncio.sleep(0.05) - except Exception as e: + except Exception: logger.critical(f"Tonlib #{self.ls_index:03d} crashed!") asyncio.ensure_future(self.restart(), loop=self.loop) await asyncio.sleep(0.05) # return result - if result and isinstance(result, dict) and ("@extra" in result) and (result["@extra"] in self.futures): + if ( + result + and isinstance(result, dict) + and ("@extra" in result) + and (result["@extra"] in self.futures) + ): try: if not self.futures[result["@extra"]].done(): self.futures[result["@extra"]].set_result(result) self.futures.pop(result["@extra"]) except Exception as e: logger.error( - f'Tonlib #{self.ls_index:03d} receiving result exception: {e}') + f"Tonlib #{self.ls_index:03d} receiving result exception: {e}" + ) self.shutdown_state = "finished" async def del_expired_futures_loop(self): diff --git a/tonsdk/provider/_tonlibjson/_sync/__init__.py b/tonsdk_ng/provider/_tonlibjson/_sync/__init__.py similarity index 64% rename from tonsdk/provider/_tonlibjson/_sync/__init__.py rename to tonsdk_ng/provider/_tonlibjson/_sync/__init__.py index f524dd8..dfd78d4 100644 --- a/tonsdk/provider/_tonlibjson/_sync/__init__.py +++ b/tonsdk_ng/provider/_tonlibjson/_sync/__init__.py @@ -2,6 +2,6 @@ from ._wrapper import SyncTonLibWrapper __all__ = [ - 'SyncTonlibClient', - 'SyncTonLibWrapper', -] \ No newline at end of file + "SyncTonlibClient", + "SyncTonLibWrapper", +] diff --git a/tonsdk/provider/_tonlibjson/_sync/_client.py b/tonsdk_ng/provider/_tonlibjson/_sync/_client.py similarity index 57% rename from tonsdk/provider/_tonlibjson/_sync/_client.py rename to tonsdk_ng/provider/_tonlibjson/_sync/_client.py index 9749388..cbde534 100644 --- a/tonsdk/provider/_tonlibjson/_sync/_client.py +++ b/tonsdk_ng/provider/_tonlibjson/_sync/_client.py @@ -5,8 +5,8 @@ from tvm_valuetypes import render_tvm_stack -from ._wrapper import SyncTonLibWrapper from .._utils import CtypesStdoutCapture, TonLibWrongResult +from ._wrapper import SyncTonLibWrapper class SyncTonlibClient: @@ -20,37 +20,41 @@ def init(self): wrapper = SyncTonLibWrapper(self.cdll_path) self.tonlib_wrapper = wrapper - one_liteserver = self.ton_config['liteservers'][random.randrange(0, len(self.ton_config['liteservers']))] - self.ton_config['liteservers'] = [one_liteserver] + one_liteserver = self.ton_config["liteservers"][ + random.randrange(0, len(self.ton_config["liteservers"])) + ] + self.ton_config["liteservers"] = [one_liteserver] with CtypesStdoutCapture(): change_verbosity_level_query = { - '@type': 'setLogVerbosityLevel', - 'new_verbosity_level': self.verbosity + "@type": "setLogVerbosityLevel", + "new_verbosity_level": self.verbosity, } keystore_obj = { - '@type': 'keyStoreTypeDirectory', - 'directory': self.keystore + "@type": "keyStoreTypeDirectory", + "directory": self.keystore, } init_tonlib_query = { - '@type': 'init', - 'options': { - '@type': 'options', - 'config': { - '@type': 'config', - 'config': json.dumps(self.ton_config), - 'use_callbacks_for_network': False, - 'blockchain_name': '', - 'ignore_cache': False + "@type": "init", + "options": { + "@type": "options", + "config": { + "@type": "config", + "config": json.dumps(self.ton_config), + "use_callbacks_for_network": False, + "blockchain_name": "", + "ignore_cache": False, }, - 'keystore_type': keystore_obj - } + "keystore_type": keystore_obj, + }, } queries_order = {} - for i, query in enumerate([change_verbosity_level_query, init_tonlib_query]): + for i, query in enumerate( + [change_verbosity_level_query, init_tonlib_query] + ): query_idx = self.__execute(query) queries_order[query_idx] = i @@ -71,10 +75,15 @@ def read_results(self, queries_order, read_timeout=None): result = None try: result = self.tonlib_wrapper.receive() - except Exception as e: # FIXME: handle exceptions (TimeOutError?) + except Exception: # FIXME: handle exceptions (TimeOutError?) raise - if result and isinstance(result, dict) and ("@extra" in result) and (result["@extra"] in queries_order): + if ( + result + and isinstance(result, dict) + and ("@extra" in result) + and (result["@extra"] in queries_order) + ): query_order = queries_order[result["@extra"]] results[query_order] = result queries_order.pop(result["@extra"]) @@ -83,49 +92,43 @@ def read_results(self, queries_order, read_timeout=None): def raw_get_account_state(self, prepared_address: str): request = { - '@type': 'raw.getAccountState', - 'account_address': { - 'account_address': prepared_address - } + "@type": "raw.getAccountState", + "account_address": {"account_address": prepared_address}, } return self.__execute(request) def raw_send_message(self, serialized_boc): - serialized_boc = codecs.decode(codecs.encode( - serialized_boc, "base64"), 'utf-8').replace("\n", '') - request = { - '@type': 'raw.sendMessage', - 'body': serialized_boc - } + serialized_boc = codecs.decode( + codecs.encode(serialized_boc, "base64"), "utf-8" + ).replace("\n", "") + request = {"@type": "raw.sendMessage", "body": serialized_boc} return self.__execute(request) def raw_run_method(self, address, method, stack_data, output_layout=None): stack_data = render_tvm_stack(stack_data) if isinstance(method, int): - method = {'@type': 'smc.methodIdNumber', 'number': method} + method = {"@type": "smc.methodIdNumber", "number": method} else: - method = {'@type': 'smc.methodIdName', 'name': str(method)} + method = {"@type": "smc.methodIdName", "name": str(method)} contract_id = self._load_contract(address) request = { - '@type': 'smc.runGetMethod', - 'id': contract_id, - 'method': method, - 'stack': stack_data + "@type": "smc.runGetMethod", + "id": contract_id, + "method": method, + "stack": stack_data, } return self.__execute(request) def _load_contract(self, address): request = { - '@type': 'smc.load', - 'account_address': { - 'account_address': address - } + "@type": "smc.load", + "account_address": {"account_address": address}, } result = self.read_result(self.__execute(request)) - if result.get('@type', 'error') == 'error': + if result.get("@type", "error") == "error": raise TonLibWrongResult("smc.load failed", result) return result["id"] diff --git a/tonsdk/provider/_tonlibjson/_sync/_wrapper.py b/tonsdk_ng/provider/_tonlibjson/_sync/_wrapper.py similarity index 84% rename from tonsdk/provider/_tonlibjson/_sync/_wrapper.py rename to tonsdk_ng/provider/_tonlibjson/_sync/_wrapper.py index 1bbcd74..8d09e14 100644 --- a/tonsdk/provider/_tonlibjson/_sync/_wrapper.py +++ b/tonsdk_ng/provider/_tonlibjson/_sync/_wrapper.py @@ -1,5 +1,5 @@ import json -from ctypes import CDLL, c_void_p, c_char_p, c_double +from ctypes import CDLL, c_char_p, c_double, c_void_p from .._utils import get_tonlib_cdll_path @@ -35,13 +35,13 @@ def __init__(self, cdll_path=None): def __del__(self): try: - if hasattr(self, '__tonlib_json_client_destroy'): + if hasattr(self, "__tonlib_json_client_destroy"): self.__tonlib_json_client_destroy(self._client) except Exception: # FIXME raise def send(self, query): - query = json.dumps(query).encode('utf-8') + query = json.dumps(query).encode("utf-8") try: self.__tonlib_json_client_send(self._client, query) @@ -51,11 +51,10 @@ def send(self, query): def receive(self, timeout=4): result = None try: - result = self.__tonlib_json_client_receive( - self._client, timeout) + result = self.__tonlib_json_client_receive(self._client, timeout) except Exception: # FIXME raise if result: - result = json.loads(result.decode('utf-8')) + result = json.loads(result.decode("utf-8")) return result diff --git a/tonsdk/provider/_tonlibjson/_utils.py b/tonsdk_ng/provider/_tonlibjson/_utils.py similarity index 74% rename from tonsdk/provider/_tonlibjson/_utils.py rename to tonsdk_ng/provider/_tonlibjson/_utils.py index 65eb965..86c3896 100644 --- a/tonsdk/provider/_tonlibjson/_utils.py +++ b/tonsdk_ng/provider/_tonlibjson/_utils.py @@ -21,20 +21,23 @@ def __str__(self): def get_tonlib_cdll_path(): platform_name = platform.system().lower() - if platform_name == 'linux': - lib_name = 'linux_libtonlibjson.so' - elif platform_name == 'darwin': - lib_name = 'macos_libtonlibjson.dylib' + if platform_name == "linux": + lib_name = "linux_libtonlibjson.so" + elif platform_name == "darwin": + lib_name = "macos_libtonlibjson.dylib" else: raise OSError("Your operating system is not supported yet") - return os.path.join( - Path(__file__).resolve().parent, 'distlib', lib_name) + return os.path.join(Path(__file__).resolve().parent, "distlib", lib_name) def get_tonlib_config_path(ton_network): return os.path.join( - Path(__file__).resolve().parent, 'distlib', 'config', f'{ton_network}.json') + Path(__file__).resolve().parent, + "distlib", + "config", + f"{ton_network}.json", + ) class CtypesStdoutCapture: @@ -67,7 +70,11 @@ def b64str_to_hex(b64str): def hex_to_b64str(x): - return codecs.encode(codecs.decode(x, 'hex'), 'base64').decode().replace("\n", "") + return ( + codecs.encode(codecs.decode(x, "hex"), "base64") + .decode() + .replace("\n", "") + ) def hash_to_hex(b64_or_hex_hash): @@ -92,23 +99,28 @@ def pubkey_b64_to_hex(b64_key): bin_key = base64.b64decode(b64_key) words = 18 ints_key = struct.unpack(f'{"H"*words}', bin_key) - key = [x.to_bytes(2, byteorder='little') for x in ints_key] - key = b''.join(key) - key = [((x & 0x0F) << 4 | (x & 0xF0) >> 4).to_bytes( - 1, byteorder='little') for x in key] - name = b''.join(key) + key = [x.to_bytes(2, byteorder="little") for x in ints_key] + key = b"".join(key) + key = [ + ((x & 0x0F) << 4 | (x & 0xF0) >> 4).to_bytes(1, byteorder="little") + for x in key + ] + name = b"".join(key) return name.hex().upper() def parallelize(f): @functools.wraps(f) def wrapper(self, *args, **kwds): - if self._style == 'futures': + if self._style == "futures": return self._executor.submit(f, self, *args, **kwds) - if self._style == 'asyncio': + if self._style == "asyncio": loop = asyncio.get_event_loop() - return loop.run_in_executor(self._executor, functools.partial(f, self, *args, **kwds)) + return loop.run_in_executor( + self._executor, functools.partial(f, self, *args, **kwds) + ) raise RuntimeError(self._style) + return wrapper @@ -118,13 +130,17 @@ def coro_result(coro): def userfriendly_to_raw(address): k = base64.urlsafe_b64decode(address)[1:34] - workchain_id = struct.unpack('b', k[:1])[0] + workchain_id = struct.unpack("b", k[:1])[0] key = k[1:].hex().upper() - return f'{workchain_id}:{key}' + return f"{workchain_id}:{key}" def str_b64encode(s): - return base64.b64encode(s.encode('utf-8')).decode('utf-8') if s and isinstance(s, str) else None + return ( + base64.b64encode(s.encode("utf-8")).decode("utf-8") + if s and isinstance(s, str) + else None + ) # repeat @@ -138,7 +154,7 @@ async def wrapper(*args, **kwargs): try: kwargs_loc = kwargs.copy() if i == repeats - 1 and last_archval: - kwargs_loc['archival'] = True + kwargs_loc["archival"] = True result = await func(*args, **kwargs_loc) exception = None except Exception as ee: @@ -146,6 +162,8 @@ async def wrapper(*args, **kwargs): if exception is not None and raise_error: raise exception return result + # end def return wrapper + return decorator diff --git a/tonsdk/provider/_utils/__init__.py b/tonsdk_ng/provider/_utils/__init__.py similarity index 93% rename from tonsdk/provider/_utils/__init__.py rename to tonsdk_ng/provider/_utils/__init__.py index d71d69e..21a3637 100644 --- a/tonsdk/provider/_utils/__init__.py +++ b/tonsdk_ng/provider/_utils/__init__.py @@ -1,11 +1,11 @@ import base64 -from .._exceptions import ResponseError from ...boc import Cell +from .._exceptions import ResponseError def parse_object(obj): - type_name = obj['@type'] + type_name = obj["@type"] if type_name in ["tvm.list", "tvm.tuple"]: return [parse_object(o) for o in obj["elements"]] # ? @@ -24,7 +24,7 @@ def parse_response_stack(pair): val = pair[1] if type_name == "num": - return int(val.replace('/0x/', ''), 16) + return int(val.replace("/0x/", ""), 16) elif type_name in ["list", "tuple"]: return parse_object(val) elif type_name == "cell": diff --git a/tonsdk/provider/_wallet.py b/tonsdk_ng/provider/_wallet.py similarity index 73% rename from tonsdk/provider/_wallet.py rename to tonsdk_ng/provider/_wallet.py index 15ab9af..fafc4dc 100644 --- a/tonsdk/provider/_wallet.py +++ b/tonsdk_ng/provider/_wallet.py @@ -5,23 +5,25 @@ def seqno_extractor(result, data): - data_cell = deserialize_boc(codecs.decode( - codecs.encode(data["data"], 'utf-8'), 'base64')) - seqno = int.from_bytes(data_cell.data.data[0:32].tobytes(), 'big') - result['seqno'] = seqno + data_cell = deserialize_boc( + codecs.decode(codecs.encode(data["data"], "utf-8"), "base64") + ) + seqno = int.from_bytes(data_cell.data.data[0:32].tobytes(), "big") + result["seqno"] = seqno def v3_extractor(result, data): seqno_extractor(result, data) - data_cell = deserialize_boc(codecs.decode( - codecs.encode(data["data"], 'utf-8'), 'base64')) - wallet_id = int.from_bytes(data_cell.data.data[32:64].tobytes(), 'big') - result['wallet_id'] = wallet_id + data_cell = deserialize_boc( + codecs.decode(codecs.encode(data["data"], "utf-8"), "base64") + ) + wallet_id = int.from_bytes(data_cell.data.data[32:64].tobytes(), "big") + result["wallet_id"] = wallet_id def sha256(x): if not isinstance(x, bytes): - x = codecs.encode(x, 'utf-8') + x = codecs.encode(x, "utf-8") h = hasher() h.update(x) return h.digest() @@ -37,13 +39,41 @@ def sha256(x): wallet_v4_r1 = "te6cckECFQEAAvUAART/APSkE/S88sgLAQIBIAIDAgFIBAUE+PKDCNcYINMf0x/THwL4I7vyY+1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8REhMUA+7QAdDTAwFxsJFb4CHXScEgkVvgAdMfIYIQcGx1Z70ighBibG5jvbAighBkc3RyvbCSXwPgAvpAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8F4ATTP8glghBwbHVnupEx4w0kghBibG5juuMABAYHCAIBIAkKAFAB+gD0BDCCEHBsdWeDHrFwgBhQBcsFJ88WUAP6AvQAEstpyx9SEMs/AFL4J28ighBibG5jgx6xcIAYUAXLBSfPFiT6AhTLahPLH1Iwyz8B+gL0AACSghBkc3Ryuo41BIEBCPRZMO1E0IEBQNcgyAHPFvQAye1UghBkc3Rygx6xcIAYUATLBVjPFiL6AhLLassfyz+UEDRfBOLJgED7AAIBIAsMAFm9JCtvaiaECAoGuQ+gIYRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQCAVgNDgARuMl+1E0NcLH4AD2ynftRNCBAUDXIfQEMALIygfL/8nQAYEBCPQKb6ExgAgEgDxAAGa3OdqJoQCBrkOuF/8AAGa8d9qJoQBBrkOuFj8AAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJcfsAyEAUgQEI9FHypwIAbIEBCNcYyFQgJYEBCPRR8qeCEG5vdGVwdIAYyMsFywJQBM8WghAF9eEA+gITy2oSyx/JcfsAAgBygQEI1xgwUgKBAQj0WfKn+CWCEGRzdHJwdIAYyMsFywJQBc8WghAF9eEA+gIUy2oTyx8Syz/Jc/sAAAr0AMntVEap808=" wallet_v4_r2 = "te6cckECFAEAAtQAART/APSkE/S88sgLAQIBIAIDAgFIBAUE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8QERITAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNBgcCASAICQB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAGAIpQBIEBCPRZMO1E0IEBQNcgyAHPFvQAye1UAXKwjiOCEGRzdHKDHrFwgBhQBcsFUAPPFiP6AhPLassfyz/JgED7AJJfA+ICASAKCwBZvSQrb2omhAgKBrkPoCGEcNQICEekk30pkQzmkD6f+YN4EoAbeBAUiYcVnzGEAgFYDA0AEbjJftRNDXCx+AA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA4PABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AAG7SB/oA1NQi+QAFyMoHFcv/ydB3dIAYyMsFywIizxZQBfoCFMtrEszMyXP7AMhAFIEBCPRR8qcCAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAAr0AMntVGliJeU=" -wallets = {sha256(wallet_v1_r1): {'type': 'wallet v1 r1', 'data_extractor': seqno_extractor}, - sha256(wallet_v1_r2): {'type': 'wallet v1 r2', 'data_extractor': seqno_extractor}, - sha256(wallet_v1_r3): {'type': 'wallet v1 r3', 'data_extractor': seqno_extractor}, - sha256(wallet_v2_r1): {'type': 'wallet v2 r1', 'data_extractor': seqno_extractor}, - sha256(wallet_v2_r2): {'type': 'wallet v2 r2', 'data_extractor': seqno_extractor}, - sha256(wallet_v3_r1): {'type': 'wallet v3 r1', 'data_extractor': v3_extractor}, - sha256(wallet_v3_r2): {'type': 'wallet v3 r2', 'data_extractor': v3_extractor}, - sha256(wallet_v4_r1): {'type': 'wallet v4 r1', 'data_extractor': v3_extractor}, - sha256(wallet_v4_r2): {'type': 'wallet v4 r2', 'data_extractor': v3_extractor}, - } +wallets = { + sha256(wallet_v1_r1): { + "type": "wallet v1 r1", + "data_extractor": seqno_extractor, + }, + sha256(wallet_v1_r2): { + "type": "wallet v1 r2", + "data_extractor": seqno_extractor, + }, + sha256(wallet_v1_r3): { + "type": "wallet v1 r3", + "data_extractor": seqno_extractor, + }, + sha256(wallet_v2_r1): { + "type": "wallet v2 r1", + "data_extractor": seqno_extractor, + }, + sha256(wallet_v2_r2): { + "type": "wallet v2 r2", + "data_extractor": seqno_extractor, + }, + sha256(wallet_v3_r1): { + "type": "wallet v3 r1", + "data_extractor": v3_extractor, + }, + sha256(wallet_v3_r2): { + "type": "wallet v3 r2", + "data_extractor": v3_extractor, + }, + sha256(wallet_v4_r1): { + "type": "wallet v4 r1", + "data_extractor": v3_extractor, + }, + sha256(wallet_v4_r2): { + "type": "wallet v4 r2", + "data_extractor": v3_extractor, + }, +} diff --git a/tonsdk_ng/utils/__init__.py b/tonsdk_ng/utils/__init__.py new file mode 100644 index 0000000..7bac0df --- /dev/null +++ b/tonsdk_ng/utils/__init__.py @@ -0,0 +1,35 @@ +from ._address import Address +from ._currency import TonCurrencyEnum, from_nano, to_nano +from ._exceptions import InvalidAddressError +from ._utils import ( + b64str_to_bytes, + b64str_to_hex, + bytes_to_b64str, + compare_bytes, + concat_bytes, + crc16, + crc32c, + move_to_end, + read_n_bytes_uint_from_array, + sign_message, + tree_walk, +) + +__all__ = [ + "Address", + "InvalidAddressError", + "concat_bytes", + "move_to_end", + "tree_walk", + "crc32c", + "crc16", + "read_n_bytes_uint_from_array", + "compare_bytes", + "sign_message", + "b64str_to_bytes", + "b64str_to_hex", + "bytes_to_b64str", + "to_nano", + "from_nano", + "TonCurrencyEnum", +] diff --git a/tonsdk/utils/_address.py b/tonsdk_ng/utils/_address.py similarity index 83% rename from tonsdk/utils/_address.py rename to tonsdk_ng/utils/_address.py index 0a37390..46c5ff5 100644 --- a/tonsdk/utils/_address.py +++ b/tonsdk_ng/utils/_address.py @@ -8,14 +8,16 @@ def parse_friendly_address(addr_str): if len(addr_str) != 48: raise InvalidAddressError( - "User-friendly address should contain strictly 48 characters") + "User-friendly address should contain strictly 48 characters" + ) # avoid padding error (https://gist.github.com/perrygeo/ee7c65bb1541ff6ac770) - data = string_to_bytes(base64.b64decode(addr_str+"==")) + data = string_to_bytes(base64.b64decode(addr_str + "==")) if len(data) != 36: raise InvalidAddressError( - "Unknown address type: byte length is not equal to 36") + "Unknown address type: byte length is not equal to 36" + ) addr = data[:34] crc = data[34:36] @@ -34,7 +36,7 @@ def parse_friendly_address(addr_str): is_bounceable = tag == Address.BOUNCEABLE_TAG - if addr[1] == 0xff: + if addr[1] == 0xFF: workchain = -1 else: workchain = addr[1] @@ -69,7 +71,7 @@ def __init__(self, any_form): return if any_form.find("-") > 0 or any_form.find("_") > 0: - any_form = any_form.replace("-", '+').replace("_", '/') + any_form = any_form.replace("-", "+").replace("_", "/") self.is_url_safe = True else: self.is_url_safe = False @@ -90,7 +92,7 @@ def __init__(self, any_form): address_hex = arr[1] if len(address_hex) != 64: - raise InvalidAddressError(f'Invalid address hex {any_form}') + raise InvalidAddressError(f"Invalid address hex {any_form}") self.is_user_friendly = False self.wc = wc @@ -105,7 +107,13 @@ def __init__(self, any_form): self.is_test_only = parse_result["is_test_only"] self.is_bounceable = parse_result["is_bounceable"] - def to_string(self, is_user_friendly=None, is_url_safe=None, is_bounceable=None, is_test_only=None): + def to_string( + self, + is_user_friendly=None, + is_url_safe=None, + is_bounceable=None, + is_test_only=None, + ): if is_user_friendly is None: is_user_friendly = self.is_user_friendly if is_url_safe is None: @@ -118,7 +126,11 @@ def to_string(self, is_user_friendly=None, is_url_safe=None, is_bounceable=None, if not is_user_friendly: return f"{self.wc}:{self.hash_part.hex()}" else: - tag = Address.BOUNCEABLE_TAG if is_bounceable else Address.NON_BOUNCEABLE_TAG + tag = ( + Address.BOUNCEABLE_TAG + if is_bounceable + else Address.NON_BOUNCEABLE_TAG + ) if is_test_only: tag |= Address.TEST_FLAG @@ -131,11 +143,13 @@ def to_string(self, is_user_friendly=None, is_url_safe=None, is_bounceable=None, address_with_checksum[:34] = addr address_with_checksum[34:] = crc16(addr) - address_base_64 = base64.b64encode( - address_with_checksum).decode('utf-8') + address_base_64 = base64.b64encode(address_with_checksum).decode( + "utf-8" + ) if is_url_safe: - address_base_64 = address_base_64.replace( - "+", '-').replace("/", '_') + address_base_64 = address_base_64.replace("+", "-").replace( + "/", "_" + ) return str(address_base_64) diff --git a/tonsdk/utils/_currency.py b/tonsdk_ng/utils/_currency.py similarity index 85% rename from tonsdk/utils/_currency.py rename to tonsdk_ng/utils/_currency.py index 23c48f4..3db3657 100644 --- a/tonsdk/utils/_currency.py +++ b/tonsdk_ng/utils/_currency.py @@ -4,20 +4,20 @@ class TonCurrencyEnum(str, Enum): - nanoton = 'nanoton' - ton = 'ton' + nanoton = "nanoton" + ton = "ton" units = { - TonCurrencyEnum.nanoton: decimal.Decimal('1'), - TonCurrencyEnum.ton: decimal.Decimal('1000000000'), + TonCurrencyEnum.nanoton: decimal.Decimal("1"), + TonCurrencyEnum.ton: decimal.Decimal("1000000000"), } integer_types = (int,) string_types = (bytes, str, bytearray) MIN_VAL = 0 -MAX_VAL = 2 ** 256 - 1 +MAX_VAL = 2**256 - 1 def is_integer(value: Any) -> bool: @@ -51,7 +51,8 @@ def to_nano(number: Union[int, float, str, decimal.Decimal], unit: str) -> int: d_number = number else: raise TypeError( - "Unsupported type. Must be one of integer, float, or string") + "Unsupported type. Must be one of integer, float, or string" + ) s_number = str(number) unit_value = units[unit.lower()] @@ -63,18 +64,19 @@ def to_nano(number: Union[int, float, str, decimal.Decimal], unit: str) -> int: with decimal.localcontext() as ctx: multiplier = len(s_number) - s_number.index(".") - 1 ctx.prec = multiplier - d_number = decimal.Decimal( - value=number, context=ctx) * 10 ** multiplier - unit_value /= 10 ** multiplier + d_number = ( + decimal.Decimal(value=number, context=ctx) * 10**multiplier + ) + unit_value /= 10**multiplier with decimal.localcontext() as ctx: ctx.prec = 999 - result_value = decimal.Decimal( - value=d_number, context=ctx) * unit_value + result_value = decimal.Decimal(value=d_number, context=ctx) * unit_value if result_value < MIN_VAL or result_value > MAX_VAL: raise ValueError( - "Resulting nanoton value must be between 1 and 2**256 - 1") + "Resulting nanoton value must be between 1 and 2**256 - 1" + ) return int(result_value) diff --git a/tonsdk/utils/_exceptions.py b/tonsdk_ng/utils/_exceptions.py similarity index 65% rename from tonsdk/utils/_exceptions.py rename to tonsdk_ng/utils/_exceptions.py index 3afbdee..62835e3 100644 --- a/tonsdk/utils/_exceptions.py +++ b/tonsdk_ng/utils/_exceptions.py @@ -2,4 +2,4 @@ class InvalidAddressError(TonSdkException): - default_detail = 'Invalid address error.' + default_detail = "Invalid address error." diff --git a/tonsdk/utils/_utils.py b/tonsdk_ng/utils/_utils.py similarity index 81% rename from tonsdk/utils/_utils.py rename to tonsdk_ng/utils/_utils.py index c9414a6..38178a3 100644 --- a/tonsdk/utils/_utils.py +++ b/tonsdk_ng/utils/_utils.py @@ -21,7 +21,9 @@ def move_to_end(index_hashmap, topological_order_arr, target): data = topological_order_arr.pop(target_index) topological_order_arr.append(data) for sub_cell in data[1].refs: - topological_order_arr, index_hashmap = move_to_end(index_hashmap, topological_order_arr, sub_cell.bytes_hash()) + topological_order_arr, index_hashmap = move_to_end( + index_hashmap, topological_order_arr, sub_cell.bytes_hash() + ) return [topological_order_arr, index_hashmap] @@ -30,20 +32,24 @@ def tree_walk(cell, topological_order_arr, index_hashmap, parent_hash=None): if cell_hash in index_hashmap: if parent_hash: if index_hashmap[parent_hash] > index_hashmap[cell_hash]: - topological_order_arr, index_hashmap = move_to_end(index_hashmap, topological_order_arr, cell_hash) + topological_order_arr, index_hashmap = move_to_end( + index_hashmap, topological_order_arr, cell_hash + ) return [topological_order_arr, index_hashmap] index_hashmap[cell_hash] = len(topological_order_arr) topological_order_arr.append([cell_hash, cell]) for sub_cell in cell.refs: - topological_order_arr, index_hashmap = tree_walk(sub_cell, topological_order_arr, index_hashmap, cell_hash) + topological_order_arr, index_hashmap = tree_walk( + sub_cell, topological_order_arr, index_hashmap, cell_hash + ) return [topological_order_arr, index_hashmap] def _crc32c(crc, bytes_arr): - POLY = 0x82f63b78 + POLY = 0x82F63B78 - crc ^= 0xffffffff + crc ^= 0xFFFFFFFF for n in range(len(bytes_arr)): crc ^= bytes_arr[n] @@ -56,7 +62,7 @@ def _crc32c(crc, bytes_arr): crc = (crc >> 1) ^ POLY if crc & 1 else crc >> 1 crc = (crc >> 1) ^ POLY if crc & 1 else crc >> 1 - return crc ^ 0xffffffff + return crc ^ 0xFFFFFFFF def crc32c(bytes_array): @@ -64,7 +70,7 @@ def crc32c(bytes_array): # TODO: check mistakes arr = bytearray(4) - struct.pack_into('>I', arr, 0, int_crc) + struct.pack_into(">I", arr, 0, int_crc) return bytes(arr)[::-1] @@ -81,8 +87,8 @@ def crc16(data): if byte & mask: reg += 1 mask >>= 1 - if reg > 0xffff: - reg &= 0xffff + if reg > 0xFFFF: + reg &= 0xFFFF reg ^= POLY return bytearray([math.floor(reg / 256), reg % 256]) @@ -116,9 +122,11 @@ def string_to_bytes(string, size=1): # ? return bytes(buf) -def sign_message(message: bytes, - signing_key, - encoder: nacl.encoding.Encoder = nacl.encoding.RawEncoder, ) -> SignedMessage: +def sign_message( + message: bytes, + signing_key, + encoder: nacl.encoding.Encoder = nacl.encoding.RawEncoder, +) -> SignedMessage: raw_signed = crypto_sign(message, signing_key) signature = encoder.encode(raw_signed[:crypto_sign_BYTES]) @@ -140,5 +148,6 @@ def b64str_to_hex(b64str): def bytes_to_b64str(bytes_arr): - return codecs.decode(codecs.encode( - bytes_arr, "base64"), 'utf-8').replace("\n", '') + return codecs.decode(codecs.encode(bytes_arr, "base64"), "utf-8").replace( + "\n", "" + )