diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6333ef0 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,23 @@ +# Pull Request Template + +## Description +Please include a summary of the change and which issue is fixed. Include the context and motivations behind your changes. + +## Type of change +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## Checklist: +Before you submit the pull request, please review the following checklist: +- [ ] I have performed a self-review of my own code. +- [ ] I have commented my code, particularly in hard-to-understand areas. +- [ ] I have made corresponding changes to the documentation. +- [ ] My changes generate no new warnings. +- [ ] I have added tests that prove my fix is effective or that my feature works. +- [ ] New and existing unit tests pass locally with my changes. +- [ ] Any dependent changes have been merged and published in downstream modules. + +## Linked Issues +Mention the issues that are addressed by this PR. You can use the `#` symbol to link an issue. \ No newline at end of file diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml new file mode 100644 index 0000000..cfb026c --- /dev/null +++ b/.github/workflows/upload.yml @@ -0,0 +1,24 @@ +name: upload + +on: + push: + tags: + - 'v*.*.*' + +permissions: + contents: write + +jobs: + upload: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: install dependencies + run: python3 -m pip install --user --upgrade poetry + - name: build + run: poetry build + - name: release + uses: softprops/action-gh-release@v1 + with: + files: | + dist/* diff --git a/.gitignore b/.gitignore index 82f9275..83a37f7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,137 +26,98 @@ share/python-wheels/ *.egg MANIFEST -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ +# Virtual environments +venv/ +ENV/ +env/ +.venv/ +*.venv -# PyBuilder -.pybuilder/ -target/ +# Poetry virtual environments +poetry.lock +.pdm.toml # Jupyter Notebook .ipynb_checkpoints -# IPython -profile_default/ -ipython_config.py +# PyCharm +.idea/ -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ +# VS Code +.vscode/ -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ +# pyenv +.python-version -# Celery stuff +# celery beat schedule file celerybeat-schedule celerybeat.pid -# SageMath parsed files -*.sage.py - -# Environments +# dotenv .env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject +.env.* -# Rope project settings -.ropeproject - -# mkdocs documentation -/site +# pyre type checker +.pyre/ -# mypy +# mypy type checker .mypy_cache/ .dmypy.json dmypy.json -# Pyre type checker -.pyre/ +# pytest +.cache/ +*.pytest_cache/ -# pytype static type analyzer -.pytype/ +# Coverage reports +htmlcov/ +.coverage +.coverage.* +.cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ -# Cython debug symbols -cython_debug/ +# pylint +pylint.log -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +# Sphinx documentation +docs/_build/ + +# Cython +*.c +*.cpp +*.cxx +*.h + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +coverage.xml +*.cover +*.py,cover +.cache + +# Miscellaneous +*.log +*.pot +*.log.* +*.out +*.pid +*.pid.lock +*.pid.txt + +# Logs and databases +*.log +*.sql +*.sqlite3 + +# Editors +*.sublime* +*~ +*~ +.*.swp diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..963354f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 120 +} diff --git a/LICENSE b/LICENSE index b56ad96..e2043f3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,7 @@ -MIT License +Copyright 2024 Crypto.com -Copyright (c) 2024 Crypto.com +Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +This project is powered by OpenAI (www.openai.com). diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5d2e73d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[tool.poetry] +name = "developer-platform-client-py" +version = "1.0.2-beta" +description = "A python client to interact with @cryptocom/developer-platform-service" +authors = ["Ric Arcifa "] +license = "MIT" +keywords = ["agent", "blockchain", "crypto.com", "AI", "CDC"] +readme = "README.md" +homepage = "https://github.com/crypto-com/developer-platform-client-py" +repository = "https://github.com/crypto-com/developer-platform-client-py" +documentation = "https://github.com/crypto-com/developer-platform-client-py" + +[tool.poetry.dependencies] +python = "^3.8.1" +requests = "^2.32.3" + +[tool.poetry.dev-dependencies] +pytest = "^8.3.3" +flake8 = "^7.1.1" +black = "^24.8.0" +isort = "^5.13.2" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 88 +target-version = ['py39'] +include = '\.pyi?$' +exclude = ''' +( + /( + \.eggs + | \.git + | \.tox + | \.venv + | build + | dist + )/ +) +''' + +[tool.isort] +profile = "black" + +[tool.poetry.scripts] +cdc-ai-agent-client = "developer-platform-client-py.cli:main" diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..79fcd5c --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,18 @@ +from .block import Block +from .client import Client +from .contract import Contract +from .interfaces.chain_interfaces import CronosEvm, CronosZkEvm +from .token import Token +from .transaction import Transaction +from .wallet import Wallet + +__all__ = [ + "Client", + "Contract", + "Wallet", + "Block", + "Transaction", + "Token", + "CronosEvm", + "CronosZkEvm", +] diff --git a/src/block.py b/src/block.py new file mode 100644 index 0000000..991acf8 --- /dev/null +++ b/src/block.py @@ -0,0 +1,35 @@ +from .client import Client +from .integrations.api_interfaces import ApiResponse +from .integrations.block_api import get_block_by_tag + + +class Block: + """ + Block class for fetching block data. + """ + _client: Client + + @classmethod + def init(cls, client: Client) -> None: + """ + Initialize the Block class with a Client instance. + + :param client: An instance of the Client class. + """ + cls._client = client + + @classmethod + def get_by_tag(cls, tag: str, tx_detail: str = "false") -> ApiResponse: + """ + Get block data by tag. + + :param tag: Integer of a block number in hex, or the string "earliest", "latest" or "pending", as in https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block + :param tx_detail: If true it returns the full transaction objects, if false only the hashes of the transactions. + :return: The block data. + """ + return get_block_by_tag( + cls._client.get_chain_id(), + cls._client.get_api_key(), + tag, + tx_detail + ) diff --git a/src/client.py b/src/client.py new file mode 100644 index 0000000..c9f09fa --- /dev/null +++ b/src/client.py @@ -0,0 +1,73 @@ + + +class Client: + """ + Client class for managing API key, chain ID and provider. + """ + + _api_key: str + _chain_id: str + _provider: str + + @classmethod + def init(cls, api_key: str, chain_id: str, provider: str = "") -> None: + """ + Initialize the client with API key and chain ID. Provider is optional. + + :param api_key: The API key for authentication. + :param chain_id: The blockchain network ID. + """ + + cls._api_key = api_key + cls._chain_id = chain_id + cls._provider = provider + + from .block import Block + from .contract import Contract + from .token import Token + from .transaction import Transaction + from .wallet import Wallet + + Contract.init(cls()) + Wallet.init(cls()) + Block.init(cls()) + Transaction.init(cls()) + Token.init(cls()) + + @classmethod + def get_api_key(cls) -> str: + """ + Get the API key. + + :return: The API key. + :raises ValueError: If the API key is not set. + """ + if cls._api_key is None: + raise ValueError("API key is not set. Please set the API key.") + + return cls._api_key + + @classmethod + def get_chain_id(cls) -> str: + """ + Get the chain ID. + + :return: The chain ID. + :raises ValueError: If the chain ID is not set. + """ + if cls._chain_id is None: + raise ValueError("Chain ID is not set. Please set the chain ID.") + + return cls._chain_id + + @classmethod + def get_provider(cls) -> str: + """ + Get the provider. + + :return: The provider. + """ + if cls._provider is None: + raise ValueError("Provider is not set. Please set the provider.") + + return cls._provider diff --git a/src/contract.py b/src/contract.py new file mode 100644 index 0000000..e1cb677 --- /dev/null +++ b/src/contract.py @@ -0,0 +1,37 @@ +from typing import Optional + +from .client import Client +from .integrations.api_interfaces import ApiResponse +from .integrations.contract_api import get_contract_abi + + +class Contract: + """ + Contract class for fetching smart contract ABIs. + """ + + _client: Optional[Client] = None + + @classmethod + def init(cls, client: Client) -> None: + """ + Initialize the Contract class with a Client instance. + + :param client: An instance of the Client class. + """ + cls._client = client + + @classmethod + def get_contract_abi(cls, contract_address: str) -> ApiResponse: + """ + Get the ABI for a smart contract. + + :param contract_address: The address of the smart contract. + """ + if cls._client is None: + raise ValueError( + "Contract class has not been initialized with a Client instance.") + + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_contract_abi(chain_id, api_key, contract_address) diff --git a/src/integrations/api_interfaces.py b/src/integrations/api_interfaces.py new file mode 100644 index 0000000..666c0f4 --- /dev/null +++ b/src/integrations/api_interfaces.py @@ -0,0 +1,12 @@ +from enum import Enum +from typing import Any, Dict, TypedDict + + +class Status(str, Enum): + SUCCESS = 'Success' + FAILED = 'Failed' + + +class ApiResponse(TypedDict): + status: Status + data: Dict[str, Any] diff --git a/src/integrations/block_api.py b/src/integrations/block_api.py new file mode 100644 index 0000000..2cb10e8 --- /dev/null +++ b/src/integrations/block_api.py @@ -0,0 +1,32 @@ +import requests + +from .api_interfaces import ApiResponse + + +def get_block_by_tag(chain_id: str, api_key: str, block_tag: str, tx_detail: str) -> ApiResponse: + """ + Get block by tag. + + :param chain_id: The ID of the blockchain network (e.g., Ethereum, Cronos). + :param api_key: The API key for authentication. + :param block_tag: The tag of the block to retrieve (e.g., "latest", "pending", "finalized"). + :param tx_detail: The detail level of transactions in the block (e.g., "full", "medium", "light"). + :return: The block data. + :rtype: ApiResponse + :raises Exception: If the block retrieval fails or the server responds with an error. + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/block/{ + chain_id}/block-tag?blockTag={block_tag}&txDetail={tx_detail}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = ( + error_body.get('error') or + f"""HTTP error! status: {response.status_code}""" + ) + raise Exception(f"""Failed to fetch block by tag: { + server_error_message}""") + + return response.json() diff --git a/src/integrations/contract_api.py b/src/integrations/contract_api.py new file mode 100644 index 0000000..ec09db3 --- /dev/null +++ b/src/integrations/contract_api.py @@ -0,0 +1,31 @@ +import requests + +from .api_interfaces import ApiResponse + + +def get_contract_abi(chain_id: str, api_key: str, contract_address: str) -> ApiResponse: + """ + Get the ABI for a smart contract. + + :param chain_id: The ID of the blockchain network + :param api_key: The API key for authentication. + :param contract_address: The address of the smart contract. + :return: The ABI of the smart contract. + :rtype: ApiResponse + :raises Exception: If the contract ABI retrieval fails or the server responds with an error. + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/contract/{ + chain_id}/contract-abi?contractAddress={contract_address}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = ( + error_body.get('error') or + f"""HTTP error! status: {response.status_code}""" + ) + raise Exception(f"""Failed to fetch contract ABI: { + server_error_message}""") + + return response.json() diff --git a/src/integrations/token_api.py b/src/integrations/token_api.py new file mode 100644 index 0000000..4977c02 --- /dev/null +++ b/src/integrations/token_api.py @@ -0,0 +1,140 @@ +from typing import Optional + +import requests + +from .api_interfaces import ApiResponse + + +def get_native_token_balance(chain_id: str, api_key: str, address: str) -> ApiResponse: + """ + Get the native token balance for a given address. + + :param chain_id: The ID of the blockchain network + :param api_key: The API key for authentication. + :param address: The address to check the balance for. + :return: The native token balance. + :rtype: ApiResponse + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/token/{ + chain_id}/native-token-balance?address={address}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to fetch native token balance: { + server_error_message}""") + + return response.json() + + +def get_erc20_token_balance(chain_id: str, api_key: str, address: str, contract_address: str, block_height: str) -> ApiResponse: + """ + Get the ERC20 token balance for a given address. + + :param chain_id: The ID of the blockchain network + :param api_key: The API key for authentication. + :param address: The address to check the balance for. + :param contract_address: The address of the ERC20 token contract. + :param block_height: The block height to check the balance at. + :return: The ERC20 token balance. + :rtype: ApiResponse + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/token/{ + chain_id}/erc20-token-balance?address={ + address}&contractAddress={contract_address}&blockHeight={block_height}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to fetch ERC20 token balance: { + server_error_message}""") + + return response.json() + + +def transfer_token(chain_id: str, payload: dict) -> ApiResponse: + """ + Transfer a token. + + :param chain_id: The ID of the blockchain network + :param payload: The payload for the transfer. + :param provider: The provider for the transfer. + :return: The transfer response. + """ + url = f"http://localhost:8000/v1/cdc-developer-platform/token/{ + chain_id}/transfer" + + response = requests.post( + url, + json=payload, + headers={'Content-Type': 'application/json'} + ) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to transfer token: { + server_error_message}""") + + return response.json() + + +def wrap_token(chain_id: str, payload: dict) -> ApiResponse: + """ + Wrap a token. + + :param chain_id: The ID of the blockchain network + :param payload: The payload for the wrap. + :param provider: The provider for the wrap. + :return: The wrap response. + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/token/{ + chain_id}/wrap""" + + response = requests.post( + url, + json=payload, + headers={'Content-Type': 'application/json'} + ) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to wrap token: {server_error_message}""") + + return response.json() + + +def swap_token(chain_id: str, payload: dict) -> ApiResponse: + """ + Swap a token. + + :param chain_id: The ID of the blockchain network + :param payload: The payload for the swap. + :param provider: The provider for the swap. + :return: The swap response. + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/token/{ + chain_id}/swap""" + + response = requests.post( + url, + json=payload, + headers={'Content-Type': 'application/json'} + ) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to swap token: {server_error_message}""") + + return response.json() diff --git a/src/integrations/transaction_api.py b/src/integrations/transaction_api.py new file mode 100644 index 0000000..3af754a --- /dev/null +++ b/src/integrations/transaction_api.py @@ -0,0 +1,51 @@ +import requests + +from .api_interfaces import ApiResponse + + +def get_transactions_by_address(chain_id: str, address: str, session: str, limit: str, api_key: str) -> ApiResponse: + url = f"""http://localhost:8000/v1/cdc-developer-platform/transaction/{ + chain_id}/address?address={address}&session={session}&limit={limit}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to fetch transactions by address: { + server_error_message}""") + + return response.json() + + +def get_transaction_by_hash(chain_id: str, tx_hash: str, api_key: str) -> ApiResponse: + url = f"""http://localhost:8000/v1/cdc-developer-platform/transaction/{ + chain_id}/tx-hash?txHash={tx_hash}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to fetch transaction by hash: { + server_error_message}""") + + return response.json() + + +def get_transaction_status(chain_id: str, tx_hash: str, api_key: str) -> ApiResponse: + url = f"""http://localhost:8000/v1/cdc-developer-platform/transaction/{ + chain_id}/status?txHash={tx_hash}&apiKey={api_key}""" + + response = requests.get(url, headers={'Content-Type': 'application/json'}) + + if response.status_code not in (200, 201): + error_body = response.json() + server_error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to fetch transaction status: { + server_error_message}""") + + return response.json() diff --git a/src/integrations/wallet_api.py b/src/integrations/wallet_api.py new file mode 100644 index 0000000..93266f7 --- /dev/null +++ b/src/integrations/wallet_api.py @@ -0,0 +1,56 @@ +from typing import Any, Dict + +import requests + +from .api_interfaces import ApiResponse + + +def create_wallet() -> ApiResponse: + """ + Creates a new wallet using the API. + + :return: The newly created wallet information. + :rtype: ApiResponse + :raises Exception: If the wallet creation fails or the server responds with an error. + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/wallet""" + + try: + response = requests.post( + url, headers={'Content-Type': 'application/json'}) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + error_message = str(e) + if response.status_code not in (200, 201): + error_body = response.json() + error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to create wallet: {error_message}""") + + +def get_balance(chain_id: str, address: str, api_key: str) -> ApiResponse: + """ + Fetches the native token balance of a wallet. + + :param chain_id: The ID of the blockchain network + :param address: The wallet address to check the balance for + :return: The native token balance of the wallet. + :rtype: ApiResponse + :raises Exception: If the fetch request fails or the server responds with an error message. + """ + url = f"""http://localhost:8000/v1/cdc-developer-platform/wallet/{ + chain_id}/balance?address={address}&api_key={api_key}""" + + try: + response = requests.get( + url, headers={'Content-Type': 'application/json'}) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + error_message = str(e) + if response.status_code not in (200, 201): + error_body = response.json() + error_message = error_body.get('error') or f"""HTTP error! status: { + response.status_code}""" + raise Exception(f"""Failed to fetch wallet balance: {error_message}""") diff --git a/src/interfaces/chain_interfaces.py b/src/interfaces/chain_interfaces.py new file mode 100644 index 0000000..d633515 --- /dev/null +++ b/src/interfaces/chain_interfaces.py @@ -0,0 +1,23 @@ +from enum import Enum + + +class CronosEvm(str, Enum): + """ + Chain IDs for Cronos EVM (Mainnet and Testnet). + """ + MAINNET = "25" + TESTNET = "338" + + def __str__(self): + return self.value + + +class CronosZkEvm(str, Enum): + """ + Chain IDs for Cronos ZK EVM (Mainnet and Testnet). + """ + MAINNET = "388" + TESTNET = "282" + + def __str__(self): + return self.value diff --git a/src/token.py b/src/token.py new file mode 100644 index 0000000..a43eb39 --- /dev/null +++ b/src/token.py @@ -0,0 +1,113 @@ +from .client import Client +from .integrations.api_interfaces import ApiResponse +from .integrations.token_api import ( + get_erc20_token_balance, + get_native_token_balance, + swap_token, + transfer_token, + wrap_token, +) + + +class Token: + """ + Token class for managing native token and ERC20 token operations. + """ + + _client: Client + + @classmethod + def init(cls, client: Client) -> None: + """ + Initialize the Token class with a Client instance. + + :param client: An instance of the Client class. + """ + cls._client = client + + @classmethod + def get_native_balance(cls, address: str) -> ApiResponse: + """ + Get the native token balance for a given address. + + :param address: The address to get the balance for. + :return: The balance of the native token. + """ + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_native_token_balance(chain_id, api_key, address) + + @classmethod + def get_erc20_balance(cls, address: str, contract_address: str, block_height: str = "latest") -> ApiResponse: + """ + Get the ERC20 token balance for a given address and contract address. + + :param address: The address to get the balance for. + :param contract_address: The contract address to get the balance for. + :param block_height: The block height to get the balance for. + :return: The balance of the ERC20 token. + """ + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_erc20_token_balance(chain_id, api_key, address, contract_address, block_height) + + @classmethod + def transfer_token(cls, to: str, amount: int) -> ApiResponse: + """ + Transfer a token to another address. + + :param to: The address to transfer the token to. + :param amount: The amount of the token to transfer. + :return: The transaction hash. + """ + chain_id = cls._client.get_chain_id() + payload = { + "to": to, + "amount": amount, + "provider": cls._client.get_provider(), + } + return transfer_token(chain_id, payload) + + @classmethod + def wrap_token(cls, from_contract_address: str, to_contract_address: str, to: str, amount: int) -> ApiResponse: + """ + Wrap a token to another address. + + :param from_contract_address: The address of the token to wrap. + :param to_contract_address: The address to wrap the token to. + :param to: The address to send the wrapped token to + :param amount: The amount of the token to wrap. + :return: The transaction hash. + """ + chain_id = cls._client.get_chain_id() + payload = { + "fromContractAddress": from_contract_address, + "toContractAddress": to_contract_address, + "amount": amount, + "to": to, + "provider": cls._client.get_provider(), + } + + return wrap_token(chain_id, payload) + + @classmethod + def swap_token(cls, from_contract_address: str, to_contract_address: str, to: str, amount: int) -> ApiResponse: + """ + Swap a token for another token. + + :param from_contract_address: The token to swap from. + :param to_contract_address: The token to swap to. + :param amount: The amount of the token to swap. + :param to: The address to send the swapped token to + :return: The transaction hash. + """ + chain_id = cls._client.get_chain_id() + payload = { + "fromContractAddress": from_contract_address, + "toContractAddress": to_contract_address, + "amount": amount, + "to": to, + "provider": cls._client.get_provider(), + } + + return swap_token(chain_id, payload) diff --git a/src/transaction.py b/src/transaction.py new file mode 100644 index 0000000..42bab37 --- /dev/null +++ b/src/transaction.py @@ -0,0 +1,60 @@ +from .client import Client +from .integrations.api_interfaces import ApiResponse +from .integrations.transaction_api import ( + get_transaction_by_hash, + get_transaction_status, + get_transactions_by_address, +) + + +class Transaction: + """ + Transaction class for handling blockchain transactions and related queries. + """ + + _client: Client + + @classmethod + def init(cls, client: Client) -> None: + """ + Initialize the Transaction class with a Client instance. + + :param client: An instance of the Client class. + """ + cls._client = client + + @classmethod + def get_transactions_by_address(cls, address: str, session: str = "", limit: str = "20") -> ApiResponse: + """ + Get transactions by address. + + :param address: The address to get transactions for. + :return: The transactions for the address. + """ + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_transactions_by_address(chain_id, address, session, limit, api_key) + + @classmethod + def get_transaction_by_hash(cls, hash: str) -> ApiResponse: + """ + Get transaction by hash. + + :param hash: The hash of the transaction. + :return: The transaction details. + """ + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_transaction_by_hash(chain_id, hash, api_key) + + @classmethod + def get_transaction_status(cls, hash: str) -> ApiResponse: + """ + Get transaction status. + + :param hash: The hash of the transaction. + :return: The transaction status. + """ + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_transaction_status(chain_id, hash, api_key) diff --git a/src/wallet.py b/src/wallet.py new file mode 100644 index 0000000..6cff09b --- /dev/null +++ b/src/wallet.py @@ -0,0 +1,41 @@ +from .client import Client +from .integrations.api_interfaces import ApiResponse +from .integrations.wallet_api import create_wallet, get_balance + + +class Wallet: + """ + Wallet class for managing wallet-related operations like creation and balance retrieval. + """ + + _client: Client + + @classmethod + def init(cls, client: Client) -> None: + """ + Initialize the Wallet class with a Client instance. + + :param client: An instance of the Client class. + """ + cls._client = client + + @classmethod + def create_wallet(cls) -> ApiResponse: + """ + Create a new wallet. + + :return: The address of the new wallet. + """ + return create_wallet() + + @classmethod + def get_balance(cls, address: str) -> ApiResponse: + """ + Get the balance of a wallet. + + :param address: The address to get the balance for. + :return: The balance of the wallet. + """ + chain_id = cls._client.get_chain_id() + api_key = cls._client.get_api_key() + return get_balance(chain_id, address, api_key)