From 699a76c5345441ad78be3314c4b41d18f5775642 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 11:01:14 +0530 Subject: [PATCH 01/10] fix: correct balance variable typo and refactor token approval method --- src/connections/ethereum_connection.py | 110 ++++++++++++------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/connections/ethereum_connection.py b/src/connections/ethereum_connection.py index cd32db00..8bb1bb63 100644 --- a/src/connections/ethereum_connection.py +++ b/src/connections/ethereum_connection.py @@ -260,7 +260,7 @@ def _get_raw_balance(self, address: str, token_address: Optional[str] = None) -> Web3.to_checksum_address(address) ).call() decimals = contract.functions.decimals().call() - return balanqce / (10 ** decimals) + return balance / (10 ** decimals) else: # Get native ETH balance balance = self._web3.eth.get_balance(Web3.to_checksum_address(address)) @@ -511,64 +511,64 @@ def _build_swap_tx( logger.error(f"Failed to build swap transaction: {str(e)}") raise - def _handle_token_approval( - self, - token_address: str, - spender_address: str, - amount: int - ) -> Optional[str]: - """Handle token approval for spender, returns tx hash if approval needed""" - try: - private_key = os.getenv('ETH_PRIVATE_KEY') - account = self._web3.eth.account.from_key(private_key) + def _handle_token_approval( + self, + token_address: str, + spender_address: str, + amount: int + ) -> Optional[str]: + """Handle token approval for spender, returns tx hash if approval needed""" + try: + private_key = os.getenv('ETH_PRIVATE_KEY') + account = self._web3.eth.account.from_key(private_key) + + token_contract = self._web3.eth.contract( + address=Web3.to_checksum_address(token_address), + abi=ERC20_ABI + ) + + # Check current allowance + current_allowance = token_contract.functions.allowance( + account.address, + spender_address + ).call() + + if current_allowance < amount: + # Prepare approval transaction + approve_tx = token_contract.functions.approve( + spender_address, + amount + ).build_transaction({ + 'from': account.address, + 'nonce': self._web3.eth.get_transaction_count(account.address), + 'gasPrice': self._web3.eth.gas_price, + 'chainId': self.chain_id + }) - token_contract = self._web3.eth.contract( - address=Web3.to_checksum_address(token_address), - abi=ERC20_ABI - ) + # Estimate gas for approval + try: + gas_estimate = self._web3.eth.estimate_gas(approve_tx) + approve_tx['gas'] = int(gas_estimate * 1.1) # Add 10% buffer + except Exception as e: + logger.warning(f"Approval gas estimation failed: {e}, using default") + approve_tx['gas'] = 100000 # Default gas for approvals - # Check current allowance - current_allowance = token_contract.functions.allowance( - account.address, - spender_address - ).call() + # Sign and send approval transaction + signed_approve = account.sign_transaction(approve_tx) + tx_hash = self._web3.eth.send_raw_transaction(signed_approve.rawTransaction) - if current_allowance < amount: - # Prepare approval transaction - approve_tx = token_contract.functions.approve( - spender_address, - amount - ).build_transaction({ - 'from': account.address, - 'nonce': self._web3.eth.get_transaction_count(account.address), - 'gasPrice': self._web3.eth.gas_price, - 'chainId': self.chain_id - }) - - # Estimate gas for approval - try: - gas_estimate = self._web3.eth.estimate_gas(approve_tx) - approve_tx['gas'] = int(gas_estimate * 1.1) # Add 10% buffer - except Exception as e: - logger.warning(f"Approval gas estimation failed: {e}, using default") - approve_tx['gas'] = 100000 # Default gas for approvals - - # Sign and send approval transaction - signed_approve = account.sign_transaction(approve_tx) - tx_hash = self._web3.eth.send_raw_transaction(signed_approve.rawTransaction) - - # Wait for approval to be mined - receipt = self._web3.eth.wait_for_transaction_receipt(tx_hash) - if receipt['status'] != 1: - raise ValueError("Token approval failed") - - return tx_hash.hex() - - return None + # Wait for approval to be mined + receipt = self._web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt['status'] != 1: + raise ValueError("Token approval failed") + + return tx_hash.hex() + + return None - except Exception as e: - logger.error(f"Token approval failed: {str(e)}") - raise + except Exception as e: + logger.error(f"Token approval failed: {str(e)}") + raise def swap( self, From d73e071f28230fa9abd256c4d77bb1b5857e4326 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 11:01:41 +0530 Subject: [PATCH 02/10] feat: add DebridgeConnection --- src/connections/debridge_connection.py | 248 +++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 src/connections/debridge_connection.py diff --git a/src/connections/debridge_connection.py b/src/connections/debridge_connection.py new file mode 100644 index 00000000..367f6c54 --- /dev/null +++ b/src/connections/debridge_connection.py @@ -0,0 +1,248 @@ +import logging +import requests +from typing import Dict, Any +import os +from dotenv import load_dotenv +from .base_connection import BaseConnection, Action, ActionParameter + +logger = logging.getLogger("connections.debridge_connection") + + +class DebridgeConnectionError(Exception): + """Base exception for Debridge connection errors""" + pass + + +class DebridgeConnection(BaseConnection): + + def __init__(self, config: Dict[str, Any]): + super().__init__(config) + self.api_url = "https://dln.debridge.finance" + self._initialize() + + def _initialize(self): + """Initialize Debridge connection""" + try: + load_dotenv() + self.access_key = os.getenv("DEBRIDGE_ACCESS_KEY") + except Exception as e: + raise DebridgeConnectionError( + f"Failed to initialize Debridge connection: {str(e)}") + + def is_llm_provider(self) -> bool: + return False + + def validate_config(self, config: Dict[str, Any]) -> Dict[str, Any]: + """Validate the configuration parameters""" + return config + + def register_actions(self) -> None: + self.actions['create-bridge-tx'] = Action( + name='create-bridge-tx', + description='Create Bridge TX using Debridge', + parameters=[ + ActionParameter(name='srcChainId', type=int, + required=True, description='Source chain ID'), + ActionParameter(name='srcChainTokenIn', type=str, + required=True, description='Source chain token address'), + ActionParameter(name='srcChainTokenInAmount', type=str, + required=True, description='Amount of source chain token'), + ActionParameter(name='dstChainId', type=int, + required=True, description='Destination chain ID'), + ActionParameter(name='dstChainTokenOut', type=str, required=True, + description='Destination chain token address'), + ActionParameter(name='dstChainTokenOutAmount', type=str, + required=True, description='Amount of destination chain token'), + ActionParameter(name='dstChainTokenOutRecipient', type=str, required=True, + description='Recipient address on destination chain'), + ActionParameter(name='srcChainOrderAuthorityAddress', type=str, required=True, + description='Source chain order authority address'), + ActionParameter(name='dstChainOrderAuthorityAddress', type=str, required=True, + description='Destination chain order authority address'), + ActionParameter(name='affiliateFeeRecipient', type=str, required=False, + description='Affiliate fee recipient address'), + ActionParameter(name='prependOperatingExpense', type=bool, + required=False, description='Prepend operating expense'), + ActionParameter(name='affiliateFeePercent', type=float, + required=False, description='Affiliate fee percentage') + ] + ) + self.actions['get-order-status'] = Action( + name='get-order-status', + description='Get order status using Debridge', + parameters=[ + ActionParameter(name='id', type=str, required=False, description='Order ID'), + ActionParameter(name='hash', type=str, required=False, description='Transaction hash') + ] + ) + self.actions['get-order-details'] = Action( + name='get-order-details', + description='Get order details using Debridge', + parameters=[ + ActionParameter(name='id', type=str, required=True, description='Order ID') + ] + ) + self.actions['cancel-tx'] = Action( + name='cancel-tx', + description='Cancel transaction using Debridge', + parameters=[ + ActionParameter(name='id', type=str, required=True, description='Order ID') + ] + ) + self.actions['extcall-cancel-tx'] = Action( + name='extcall-cancel-tx', + description='External call to cancel transaction using Debridge', + parameters=[ + ActionParameter(name='id', type=str, required=True, description='Order ID') + ] + ) + self.actions['get-supported-chains'] = Action( + name='get-supported-chains', + description='Get supported chains using Debridge', + parameters=[] + ) + self.actions['get-token-list'] = Action( + name='get-token-list', + description='Get token list using Debridge', + parameters=[ + ActionParameter(name='chainId', type=int, required=True, description='Chain ID') + ] + ) + + def configure(self) -> bool: + """Configure the Debridge connection""" + try: + self._initialize() + return True + except Exception as e: + logger.error(f"Failed to configure Debridge connection: {str(e)}") + return False + + def is_configured(self, verbose: bool = False) -> bool: + """Check if the connection is properly configured""" + try: + # TODO: Add any additional checks if needed + return True + except Exception as e: + if verbose: + logger.error(f"Configuration check failed: {str(e)}") + return False + + def create_bridge_tx( + self, + srcChainId: int, + srcChainTokenIn: str, + dstChainId: int, + dstChainTokenOut: str, + dstChainTokenOutRecipient: str, + srcChainOrderAuthorityAddress: str, + dstChainOrderAuthorityAddress: str, + affiliateFeeRecipient: str, + prependOperatingExpense: bool = True, + srcChainTokenInAmount: str = "auto", + dstChainTokenOutAmount: str = "auto", + affiliateFeePercent: float = 0 + ) -> Dict: + """Create Bridge TX using Debridge""" + try: + params = { + "srcChainId": srcChainId, + "srcChainTokenIn": srcChainTokenIn, + "srcChainTokenInAmount": srcChainTokenInAmount, + "dstChainId": dstChainId, + "dstChainTokenOut": dstChainTokenOut, + "dstChainTokenOutAmount": dstChainTokenOutAmount, + "prependOperatingExpense": prependOperatingExpense, + "dstChainTokenOutRecipient": dstChainTokenOutRecipient, + "srcChainOrderAuthorityAddress": srcChainOrderAuthorityAddress, + "dstChainOrderAuthorityAddress": dstChainOrderAuthorityAddress, + "affiliateFeePercent": affiliateFeePercent, + "affiliateFeeRecipient": affiliateFeeRecipient, + "accesstoken": self.access_key + } + + response = requests.get(f"{self.api_url}/v1.0/dln/order/create-tx", params=params) + response.raise_for_status() + return response.json() + + except Exception as e: + raise DebridgeConnectionError(f"Failed to bridge assets: {str(e)}") + + def get_order_status(self, id: str = None, hash: str = None) -> Dict: + """Get order status using Debridge""" + try: + if (id): + response = requests.get(f"{self.api_url}/v1.0/dln/order/{id}/status", params={"accesstoken": self.access_key}) + elif (hash): + response = requests.get(f"{self.api_url}/v1.0/dln/tx/{hash}/order-ids", params={"accesstoken": self.access_key}) + else: + raise ValueError("Either 'id' or 'hash' must be provided") + + response.raise_for_status() + return response.json() + except Exception as e: + raise DebridgeConnectionError(f"Failed to get order status: {str(e)}") + + def get_order_details(self, id: str) -> Dict: + """Get order details using Debridge""" + try: + response = requests.get(f"{self.api_url}/v1.0/dln/order/{id}", params={"accesstoken": self.access_key}) + response.raise_for_status() + return response.json() + except Exception as e: + raise DebridgeConnectionError(f"Failed to get order details: {str(e)}") + + def cancel_tx(self, id: str) -> Dict: + """Cancel transaction using Debridge""" + try: + response = requests.post(f"{self.api_url}/v1.0/dln/order/{id}/cancel-tx", params={"accesstoken": self.access_key}) + response.raise_for_status() + return response.json() + except Exception as e: + raise DebridgeConnectionError(f"Failed to cancel transaction: {str(e)}") + + def extcall_cancel_tx(self, id: str) -> Dict: + """External call to cancel transaction using Debridge""" + try: + response = requests.post(f"{self.api_url}/v1.0/dln/order/{id}/extcall-cancel-tx", params={"accesstoken": self.access_key}) + response.raise_for_status() + return response.json() + except Exception as e: + raise DebridgeConnectionError(f"Failed to perform external call to cancel transaction: {str(e)}") + + def get_supported_chains(self) -> Dict: + """Get supported chains using Debridge""" + try: + response = requests.get(f"{self.api_url}/v1.0/supported-chains-info", params={"accesstoken": self.access_key}) + response.raise_for_status() + return response.json() + except Exception as e: + raise DebridgeConnectionError(f"Failed to get supported chains: {str(e)}") + + def get_token_list(self, chainId: int) -> Dict: + """Get token list using Debridge""" + try: + response = requests.get(f"{self.api_url}/v1.0/token-list", params={"chainId": chainId, "accesstoken": self.access_key}) + response.raise_for_status() + return response.json() + except Exception as e: + raise DebridgeConnectionError(f"Failed to get token list: {str(e)}") + + def perform_action(self, action_name: str, kwargs) -> Any: + """Execute a Debridge action with validation""" + if action_name not in self.actions: + raise KeyError(f"Unknown action: {action_name}") + + if not self.is_configured(verbose=True): + raise DebridgeConnectionError( + "Debridge is not properly configured") + + action = self.actions[action_name] + validation_errors = action.validate_params(kwargs) + if validation_errors: + raise DebridgeConnectionError( + f"Invalid parameters: {', '.join(validation_errors)}") + + method_name = action_name.replace('-', '_') + method = getattr(self, method_name) + return method(**kwargs) From e516142c65107bf53f4ca07212a000ec62a7eead Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 11:07:19 +0530 Subject: [PATCH 03/10] feat: configure debridge connection --- .env.example | 1 + agents/example.json | 4 +- poetry.lock | 126 ++++++++++++++++++++++++++++++++++++-- src/connection_manager.py | 3 + 4 files changed, 129 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index a217af8d..acfebecf 100644 --- a/.env.example +++ b/.env.example @@ -23,4 +23,5 @@ SOLANA_PRIVATE_KEY= DISCORD_TOKEN= XAI_API_KEY= TOGETHER_API_KEY= +DEBRIDGE_ACCESS_KEY= diff --git a/agents/example.json b/agents/example.json index 1fb89e84..fd15634f 100644 --- a/agents/example.json +++ b/agents/example.json @@ -103,7 +103,9 @@ "name": "evm", "network": "ethereum" }, - + { + "name": "debridge" + }, { "name": "discord", "message_read_count": 10, diff --git a/poetry.lock b/poetry.lock index 20372526..ef0486a5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,6 +6,7 @@ version = "2.4.4" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, @@ -17,6 +18,7 @@ version = "3.11.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, @@ -114,6 +116,7 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -128,6 +131,7 @@ version = "0.1.0" description = "Allora Network SDK" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "allora_sdk-0.1.0-py3-none-any.whl", hash = "sha256:0e5b644918e35e097f23f16e280318ad5c23d911efc66633b18af961552faecb"}, {file = "allora_sdk-0.1.0.tar.gz", hash = "sha256:642777629473120d5f20b4e2fc62490bfe35b4552e28a083866d121ec4623589"}, @@ -165,6 +169,7 @@ version = "0.20.1" description = "The Python Anchor client." optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "anchorpy-0.20.1-py3-none-any.whl", hash = "sha256:78c82b56e340240fd00697cde08cd63f84ccc48b34532f0eed4a570f7054aec9"}, {file = "anchorpy-0.20.1.tar.gz", hash = "sha256:e4ac7e3e742a4a31165da2cdded44bfec8d03802488beb36f6735d1c9351fe08"}, @@ -193,6 +198,7 @@ version = "0.2.0" description = "Python bindings for Anchor Rust code" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "anchorpy_core-0.2.0-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f04928c0916e8a5cba5986a85db682df19e204bb424bdea49b8a46fc45f2fc66"}, {file = "anchorpy_core-0.2.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e37373336fa735f7f3f5eab8f3b090c8a39ef48f3f4f367ad703c7643260560"}, @@ -216,6 +222,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -227,6 +234,7 @@ version = "0.42.0" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "anthropic-0.42.0-py3-none-any.whl", hash = "sha256:46775f65b723c078a2ac9e9de44a46db5c6a4fabeacfd165e5ea78e6817f4eff"}, {file = "anthropic-0.42.0.tar.gz", hash = "sha256:bf8b0ed8c8cb2c2118038f29c58099d2f99f7847296cafdaa853910bfff4edf4"}, @@ -251,6 +259,7 @@ version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, @@ -272,6 +281,7 @@ version = "3.4.3" description = "reference implementation of PEP 3156" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"}, {file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"}, @@ -285,6 +295,7 @@ version = "25.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, @@ -304,6 +315,7 @@ version = "2.1.1" description = "Base58 and Base58Check implementation." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, @@ -318,6 +330,7 @@ version = "0.1.1" description = "A fast Python library for Base58 and Base58Check" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "based58-0.1.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:745851792ce5fada615f05ec61d7f360d19c76950d1e86163b2293c63a5d43bc"}, {file = "based58-0.1.1-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f8448a71678bd1edc0a464033695686461ab9d6d0bc3282cb29b94f883583572"}, @@ -343,6 +356,7 @@ version = "3.0.0" description = "efficient arrays of booleans -- C extension" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "bitarray-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5ddbf71a97ad1d6252e6e93d2d703b624d0a5b77c153b12f9ea87d83e1250e0c"}, {file = "bitarray-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0e7f24a0b01e6e6a0191c50b06ca8edfdec1988d9d2b264d669d2487f4f4680"}, @@ -489,6 +503,7 @@ version = "0.1.0" description = "Python implementation of Borsh serialization, built on the Construct library." optional = false python-versions = ">=3.8.3,<4.0.0" +groups = ["main"] files = [ {file = "borsh-construct-0.1.0.tar.gz", hash = "sha256:c916758ceba70085d8f456a1cc26991b88cb64233d347767766473b651b37263"}, {file = "borsh_construct-0.1.0-py3-none-any.whl", hash = "sha256:f584c791e2a03f8fc36e6c13011a27bcaf028c9c54ba89cd70f485a7d1c687ed"}, @@ -504,6 +519,7 @@ version = "2.0.1" description = "A decorator for caching properties in classes." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb"}, {file = "cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641"}, @@ -515,6 +531,7 @@ version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, @@ -526,6 +543,7 @@ version = "2.0.0" description = "Canonical JSON" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "canonicaljson-2.0.0-py3-none-any.whl", hash = "sha256:c38a315de3b5a0532f1ec1f9153cd3d716abfc565a558d00a4835428a34fca5b"}, {file = "canonicaljson-2.0.0.tar.gz", hash = "sha256:e2fdaef1d7fadc5d9cb59bd3d0d41b064ddda697809ac4325dced721d12f113f"}, @@ -537,6 +555,7 @@ version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, @@ -548,6 +567,7 @@ version = "5.2.0" description = "Universal encoding detector for Python 3" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, @@ -559,6 +579,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -660,6 +681,7 @@ version = "1.0.2" description = "Python bindings for C-KZG-4844" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "ckzg-1.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bdd082bc0f2a595e3546658ecbe1ff78fe65b0ab7e619a8197a62d94f46b5b46"}, {file = "ckzg-1.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50ca4af4e2f1a1e8b0a7e97b3aef39dedbb0d52d90866ece424f13f8df1b5972"}, @@ -754,6 +776,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -768,6 +791,7 @@ 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" +groups = ["main"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -779,6 +803,7 @@ version = "2.10.68" description = "A powerful declarative symmetric parser/builder for binary data" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "construct-2.10.68.tar.gz", hash = "sha256:7b2a3fd8e5f597a5aa1d614c3bd516fa065db01704c72a1efaaeec6ef23d8b45"}, ] @@ -792,6 +817,7 @@ version = "0.5.6" description = "Extension for the python package 'construct' that adds typing features" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "construct-typing-0.5.6.tar.gz", hash = "sha256:0dc501351cd6b308f15ec54e5fe7c0fbc07cc1530a1b77b4303062a0a93c1297"}, {file = "construct_typing-0.5.6-py3-none-any.whl", hash = "sha256:39c948329e880564e33521cba497b21b07967c465b9c9037d6334e2cffa1ced9"}, @@ -806,6 +832,8 @@ version = "1.0.1" description = "Cython implementation of Toolz: High performance functional utilities" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name == \"cpython\"" files = [ {file = "cytoolz-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cec9af61f71fc3853eb5dca3d42eb07d1f48a4599fa502cbe92adde85f74b042"}, {file = "cytoolz-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:140bbd649dbda01e91add7642149a5987a7c3ccc251f2263de894b89f50b6608"}, @@ -921,6 +949,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -932,6 +961,7 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -943,6 +973,7 @@ version = "5.0.1" description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" optional = false python-versions = ">=3.8, <4" +groups = ["main"] files = [ {file = "eth_abi-5.0.1-py3-none-any.whl", hash = "sha256:521960d8b4beee514958e1774951dc6b48176aa274e3bd8b166f6921453047ef"}, {file = "eth_abi-5.0.1.tar.gz", hash = "sha256:e9425110c6120c585c9f0db2e8a33d76c4b886b148a65e68fc0035d3917a3b9c"}, @@ -965,6 +996,7 @@ version = "0.10.0" description = "eth-account: Sign Ethereum transactions and messages with local private keys" optional = false python-versions = ">=3.7, <4" +groups = ["main"] files = [ {file = "eth-account-0.10.0.tar.gz", hash = "sha256:474a2fccf7286230cf66502565f03b536921d7e1fdfceba198e42160e5ac4bc1"}, {file = "eth_account-0.10.0-py3-none-any.whl", hash = "sha256:b7a83f506a8edf57926569e5f04471ce3f1700e572d3421b4ad0dad7a26c0978"}, @@ -992,6 +1024,7 @@ version = "0.7.1" description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_hash-0.7.1-py3-none-any.whl", hash = "sha256:0fb1add2adf99ef28883fd6228eb447ef519ea72933535ad1a0b28c6f65f868a"}, {file = "eth_hash-0.7.1.tar.gz", hash = "sha256:d2411a403a0b0a62e8247b4117932d900ffb4c8c64b15f92620547ca5ce46be5"}, @@ -1013,6 +1046,7 @@ version = "0.9.0" description = "eth-keyfile: A library for handling the encrypted keyfiles used to store ethereum private keys" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_keyfile-0.9.0-py3-none-any.whl", hash = "sha256:45d3513b6433ad885370225ba0429ed26493ba23589c5b1ca5da024765020fef"}, {file = "eth_keyfile-0.9.0.tar.gz", hash = "sha256:8621c35e83cbc05909d2f23dbb8a87633918733caea553ae0e298f6a06291526"}, @@ -1035,6 +1069,7 @@ version = "0.6.1" description = "eth-keys: Common API for Ethereum key operations" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_keys-0.6.1-py3-none-any.whl", hash = "sha256:7deae4cd56e862e099ec58b78176232b931c4ea5ecded2f50c7b1ccbc10c24cf"}, {file = "eth_keys-0.6.1.tar.gz", hash = "sha256:a43e263cbcabfd62fa769168efc6c27b1f5603040e4de22bb84d12567e4fd962"}, @@ -1056,6 +1091,7 @@ version = "1.0.1" description = "eth-rlp: RLP definitions for common Ethereum objects in Python" optional = false python-versions = ">=3.8, <4" +groups = ["main"] files = [ {file = "eth-rlp-1.0.1.tar.gz", hash = "sha256:d61dbda892ee1220f28fb3663c08f6383c305db9f1f5624dc585c9cd05115027"}, {file = "eth_rlp-1.0.1-py3-none-any.whl", hash = "sha256:dd76515d71654277377d48876b88e839d61553aaf56952e580bb7cebef2b1517"}, @@ -1078,6 +1114,7 @@ version = "4.4.0" description = "eth-typing: Common type annotations for ethereum python packages" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_typing-4.4.0-py3-none-any.whl", hash = "sha256:a5e30a6e69edda7b1d1e96e9d71bab48b9bb988a77909d8d1666242c5562f841"}, {file = "eth_typing-4.4.0.tar.gz", hash = "sha256:93848083ac6bb4c20cc209ea9153a08b0a528be23337c889f89e1e5ffbe9807d"}, @@ -1097,6 +1134,7 @@ version = "4.1.1" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "eth_utils-4.1.1-py3-none-any.whl", hash = "sha256:ccbbac68a6d65cb6e294c5bcb6c6a5cec79a241c56dc5d9c345ed788c30f8534"}, {file = "eth_utils-4.1.1.tar.gz", hash = "sha256:71c8d10dec7494aeed20fa7a4d52ec2ce4a2e52fdce80aab4f5c3c19f3648b25"}, @@ -1119,6 +1157,7 @@ version = "0.2.2" description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, @@ -1133,6 +1172,7 @@ version = "0.1.3" description = "Packaged metadata on Ethereum Virtual Machine (EVM) chains" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "evmchains-0.1.3-py3-none-any.whl", hash = "sha256:6dc608a24af2bc2d05fd186ce8ccdba69af606ab46df3cb22cc30754889871b5"}, {file = "evmchains-0.1.3.tar.gz", hash = "sha256:1a3225a34a18fe35d67a8c344a9ee5c7453c73b5e637a7cdc919046632707675"}, @@ -1150,6 +1190,7 @@ version = "0.7.11" description = "farcaster-py is a Python SDK for the Farcaster Protocol" optional = false python-versions = ">=3.8.0,<4.0.0" +groups = ["main"] files = [ {file = "farcaster-0.7.11-py3-none-any.whl", hash = "sha256:b830b8f7683fd01a8d828cac71c59bc3410aa7758c73bdfa391610f9f2bb1517"}, {file = "farcaster-0.7.11.tar.gz", hash = "sha256:45746e3d1718bed6dd231764da0ad5805f643a23e1a4a07907a8970c4e28e6bf"}, @@ -1170,6 +1211,8 @@ version = "0.109.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"server\"" files = [ {file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"}, {file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"}, @@ -1189,6 +1232,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -1205,6 +1249,7 @@ version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -1306,6 +1351,7 @@ version = "0.1.3" description = "Goat 🐐 (Great Onchain Agent Toolkit) is an open-source framework for connecting AI agents to any onchain app" optional = false python-versions = "<4.0,>=3.10" +groups = ["main"] files = [ {file = "goat_sdk-0.1.3-py3-none-any.whl", hash = "sha256:6d0a0b7eb49b35ef4fd1b020eded26879e0a126a4874e21b46058f00698b97f0"}, {file = "goat_sdk-0.1.3.tar.gz", hash = "sha256:5f852a4409de413e15ee69704fe8baf335eefc704c7797f4830b2d0e5ae9f1cd"}, @@ -1322,6 +1368,7 @@ version = "0.1.1" description = "Goat plugin for Coingecko" optional = false python-versions = "<4.0,>=3.10" +groups = ["main"] files = [ {file = "goat_sdk_plugin_coingecko-0.1.1-py3-none-any.whl", hash = "sha256:6f15f9a5c9096fabb46700dea3efcb267eda1eb5bbb443c9294287ff0ef7a919"}, {file = "goat_sdk_plugin_coingecko-0.1.1.tar.gz", hash = "sha256:3a0b72ad95202415bb0b26b6581c93aa833e5b23542afda6fe34251bc2421a97"}, @@ -1337,6 +1384,7 @@ version = "0.1.0" description = "Goat plugin for ERC20" optional = false python-versions = "<4.0,>=3.10" +groups = ["main"] files = [ {file = "goat_sdk_plugin_erc20-0.1.0-py3-none-any.whl", hash = "sha256:04269a0219aeebaa396812a19d8c765a7d9f3e649cae9126790cebb1183e3b4a"}, {file = "goat_sdk_plugin_erc20-0.1.0.tar.gz", hash = "sha256:887dc2dbb2e29a0b9c3cff721a4f841407b2a70edab0f2097391dc8320bb15f3"}, @@ -1352,6 +1400,7 @@ version = "0.1.1" description = "Goat SDK EVM wallet implementation" optional = false python-versions = "<4.0,>=3.10" +groups = ["main"] files = [ {file = "goat_sdk_wallet_evm-0.1.1-py3-none-any.whl", hash = "sha256:41143367fca696e5fdd6d6b09639a691eb8f4ebebef4248531c012f3497ab1ee"}, {file = "goat_sdk_wallet_evm-0.1.1.tar.gz", hash = "sha256:55840ebb625a34aa4b300d52e197e4e739c33a6630952bb00cea1d27ff28c16b"}, @@ -1368,6 +1417,7 @@ version = "0.1.1" description = "Goat SDK Web3 wallet implementation" optional = false python-versions = "<4.0,>=3.10" +groups = ["main"] files = [ {file = "goat_sdk_wallet_web3-0.1.1-py3-none-any.whl", hash = "sha256:e2faaf28e360379caaf8846a48e4682a636fa1e656fc443816eb32f78755bc68"}, {file = "goat_sdk_wallet_web3-0.1.1.tar.gz", hash = "sha256:b873e9741d5ede11e6da5655f9e5512d630e558b52593487f4be346cd9506140"}, @@ -1384,6 +1434,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -1395,6 +1446,7 @@ version = "0.3.1" description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" optional = false python-versions = ">=3.7, <4" +groups = ["main"] files = [ {file = "hexbytes-0.3.1-py3-none-any.whl", hash = "sha256:383595ad75026cf00abd570f44b368c6cdac0c6becfae5c39ff88829877f8a59"}, {file = "hexbytes-0.3.1.tar.gz", hash = "sha256:a3fe35c6831ee8fafd048c4c086b986075fc14fd46258fa24ecb8d65745f9a9d"}, @@ -1412,6 +1464,7 @@ version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, @@ -1433,6 +1486,7 @@ version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -1457,6 +1511,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1471,6 +1526,7 @@ version = "0.8.2" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, @@ -1556,6 +1612,7 @@ version = "0.1.1" description = "A microlibrary that defines a Json type alias for Python." optional = false python-versions = ">=3.7,<4.0" +groups = ["main"] files = [ {file = "jsonalias-0.1.1-py3-none-any.whl", hash = "sha256:a56d2888e6397812c606156504e861e8ec00e188005af149f003c787db3d3f18"}, {file = "jsonalias-0.1.1.tar.gz", hash = "sha256:64f04d935397d579fc94509e1fcb6212f2d081235d9d6395bd10baedf760a769"}, @@ -1567,6 +1624,7 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -1588,6 +1646,7 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -1602,6 +1661,7 @@ version = "0.0.2.0" description = "Jupiter Python SDK" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "jupiter-python-sdk-0.0.2.0.tar.gz", hash = "sha256:5e055106f62cdbe33e7867bbcfcab8b15486be92543555b40dc84113685551f6"}, {file = "jupiter_python_sdk-0.0.2.0-py3-none-any.whl", hash = "sha256:a4fc7bd3cd663c9bb9f35a1710555e9392ebe48ca0417c16fe8a701b28777a00"}, @@ -1620,6 +1680,7 @@ version = "1.2.0" description = "An Dict like LRU container." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "lru-dict-1.2.0.tar.gz", hash = "sha256:13c56782f19d68ddf4d8db0170041192859616514c706b126d0df2ec72a11bd7"}, {file = "lru_dict-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:de906e5486b5c053d15b7731583c25e3c9147c288ac8152a6d1f9bccdec72641"}, @@ -1714,6 +1775,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -1738,6 +1800,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1749,6 +1812,7 @@ version = "8.14.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "more-itertools-8.14.0.tar.gz", hash = "sha256:c09443cd3d5438b8dafccd867a6bc1cb0894389e90cb53d227456b0b0bccb750"}, {file = "more_itertools-8.14.0-py3-none-any.whl", hash = "sha256:1bc4f91ee5b1b31ac7ceacc17c09befe6a40a503907baf9c839c229b5095cfd2"}, @@ -1760,6 +1824,7 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -1861,6 +1926,7 @@ version = "2.2.2" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"}, {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"}, @@ -1925,6 +1991,7 @@ version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, @@ -1941,6 +2008,7 @@ version = "1.61.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "openai-1.61.0-py3-none-any.whl", hash = "sha256:e8c512c0743accbdbe77f3429a1490d862f8352045de8dc81969301eb4a4f666"}, {file = "openai-1.61.0.tar.gz", hash = "sha256:216f325a24ed8578e929b0f1b3fb2052165f3b04b0461818adaa51aa29c71f8a"}, @@ -1966,6 +2034,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1977,6 +2046,7 @@ version = "0.9.0" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "parsimonious-0.9.0.tar.gz", hash = "sha256:b2ad1ae63a2f65bd78f5e0a8ac510a98f3607a43f1db2a8d46636a5d9e4a30c1"}, ] @@ -1990,6 +2060,7 @@ version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, @@ -2087,6 +2158,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -2103,6 +2175,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -2118,6 +2191,7 @@ version = "3.0.50" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, @@ -2132,6 +2206,7 @@ version = "0.2.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, @@ -2223,6 +2298,7 @@ version = "5.29.3" description = "" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, @@ -2243,6 +2319,7 @@ version = "7.0.1" description = "py-ecc: Elliptic curve crypto in python including secp256k1, alt_bn128, and bls12_381" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "py_ecc-7.0.1-py3-none-any.whl", hash = "sha256:84a8b4d436163c83c65345a68e32f921ef6e64374a36f8e561f0455b4b08f5f2"}, {file = "py_ecc-7.0.1.tar.gz", hash = "sha256:557461f42e57294d734305a30faf6b8903421651871e9cdeff8d8e67c6796c70"}, @@ -2264,6 +2341,7 @@ version = "19.0.0" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pyarrow-19.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c318eda14f6627966997a7d8c374a87d084a94e4e38e9abbe97395c215830e0c"}, {file = "pyarrow-19.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:62ef8360ff256e960f57ce0299090fb86423afed5e46f18f1225f960e05aae3d"}, @@ -2318,6 +2396,7 @@ version = "3.21.0" description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] files = [ {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, @@ -2359,6 +2438,7 @@ version = "2.10.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, @@ -2379,6 +2459,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -2491,6 +2572,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2505,6 +2587,7 @@ version = "0.1.5" description = "Python bindings for heck, the Rust case conversion library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pyheck-0.1.5-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:44caf2b7a49d71fdeb0469e9f35886987ad815a8638b3c5b5c83f351d6aed413"}, {file = "pyheck-0.1.5-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:316a842b94beff6e59a97dbcc590e9be92a932e59126b0faa9ac750384f27eaf"}, @@ -2530,6 +2613,7 @@ version = "3.8.0" description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, @@ -2541,6 +2625,7 @@ version = "1.8.0" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, @@ -2559,6 +2644,7 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -2573,6 +2659,7 @@ version = "16.0.0" description = "Unicode normalization forms (NFC, NFKC, NFD, NFKD). A library independent of the Python core Unicode database." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "pyunormalize-16.0.0-py3-none-any.whl", hash = "sha256:c647d95e5d1e2ea9a2f448d1d95d8518348df24eab5c3fd32d2b5c3300a49152"}, {file = "pyunormalize-16.0.0.tar.gz", hash = "sha256:2e1dfbb4a118154ae26f70710426a52a364b926c9191f764601f5a8cb12761f7"}, @@ -2584,6 +2671,8 @@ version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\"" files = [ {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, @@ -2611,6 +2700,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -2627,6 +2717,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -2730,6 +2821,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2751,6 +2843,7 @@ version = "1.3.1" description = "OAuthlib authentication support for Requests." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] files = [ {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, @@ -2769,6 +2862,7 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -2777,7 +2871,6 @@ files = [ [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -2788,6 +2881,7 @@ version = "4.0.1" description = "rlp: A package for Recursive Length Prefix encoding and decoding" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "rlp-4.0.1-py3-none-any.whl", hash = "sha256:ff6846c3c27b97ee0492373aa074a7c3046aadd973320f4fffa7ac45564b0258"}, {file = "rlp-4.0.1.tar.gz", hash = "sha256:bcefb11013dfadf8902642337923bd0c786dc8a27cb4c21da6e154e52869ecb1"}, @@ -2808,6 +2902,7 @@ version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, @@ -2920,6 +3015,7 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -2931,6 +3027,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -2942,6 +3039,7 @@ version = "0.35.1" description = "Solana Python API" optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "solana-0.35.1-py3-none-any.whl", hash = "sha256:f1388142f8e7edcf3fba52ddd021bab5ceaeab491b5100203732f40963f3742f"}, {file = "solana-0.35.1.tar.gz", hash = "sha256:8768246c66f9b8aebcb85cb89465240cc42aab3bf802c15ebae2408f2076eaa1"}, @@ -2960,6 +3058,7 @@ version = "0.21.0" description = "Python bindings for Solana Rust tools" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "solders-0.21.0-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7df88e59aea016644c0b2eac84f2f931d5aa570c654132770263b26f2928fdb7"}, {file = "solders-0.21.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a11dfc5933707c466880ef2116f1bffc74659bf677b79479f4280247d60543c9"}, @@ -2982,6 +3081,8 @@ version = "0.36.3" description = "The little ASGI library that shines." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"server\"" files = [ {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, @@ -2999,6 +3100,7 @@ version = "0.1a6" description = "Algebraic types for Python (notably providing Sum Types, aka Tagged Unions)" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "sumtypes-0.1a6-py2.py3-none-any.whl", hash = "sha256:3e9d71322dd927d25d935072f8be7daec655ea292fd392359a5bb2c1e53dfdc3"}, {file = "sumtypes-0.1a6.tar.gz", hash = "sha256:1a6ff095e06a1885f340ddab803e0f38e3f9bed81f9090164ca9682e04e96b43"}, @@ -3013,6 +3115,7 @@ version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -3027,6 +3130,7 @@ version = "1.3.14" description = "Python client for Together's Cloud Platform!" optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "together-1.3.14-py3-none-any.whl", hash = "sha256:da3ca1247072878728b7fba69c23a7852cc32d4eec89d14c79505c0a97b9ae1c"}, {file = "together-1.3.14.tar.gz", hash = "sha256:9ce173fd11253dfa1a34dfcd48f77be7ed82ac643b07d96157f0f65356b9c76d"}, @@ -3056,6 +3160,7 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -3067,6 +3172,7 @@ version = "0.11.2" description = "List processing tools and functional utilities" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "toolz-0.11.2-py3-none-any.whl", hash = "sha256:a5700ce83414c64514d82d60bcda8aabfde092d1c1a8663f9200c07fdcc6da8f"}, {file = "toolz-0.11.2.tar.gz", hash = "sha256:6b312d5e15138552f1bda8a4e66c30e236c831b612b2bf0005f8a1df10a4bc33"}, @@ -3078,6 +3184,7 @@ version = "4.23.2" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"}, {file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"}, @@ -3103,6 +3210,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -3124,6 +3232,7 @@ version = "0.15.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, @@ -3141,6 +3250,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -3152,6 +3262,7 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -3169,6 +3280,8 @@ version = "0.27.1" description = "The lightning-fast ASGI server." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"server\"" files = [ {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, @@ -3187,6 +3300,7 @@ version = "20.28.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, @@ -3207,6 +3321,7 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -3218,6 +3333,7 @@ version = "6.20.3" description = "web3.py" optional = false python-versions = ">=3.7.2" +groups = ["main"] files = [ {file = "web3-6.20.3-py3-none-any.whl", hash = "sha256:529fbb33f2476ce8185f7a2ed7e2e07c4c28621b0e89b845fbfdcaea9571286d"}, {file = "web3-6.20.3.tar.gz", hash = "sha256:c69dbf1a61ace172741d06990e60afc7f55f303eac087e7235f382df3047d017"}, @@ -3253,6 +3369,7 @@ version = "10.4" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, @@ -3331,6 +3448,7 @@ version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -3425,6 +3543,6 @@ propcache = ">=0.2.0" server = ["fastapi", "requests", "uvicorn"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.11" -content-hash = "54c122c4f40fce6a813645e76e7005a56b83846bc14149a0375a9bf53cade3b4" \ No newline at end of file +content-hash = "5cea3b00393c74cad58a099d7c9019ffdb34fc357016b717ee4ef258f7fd0a58" diff --git a/src/connection_manager.py b/src/connection_manager.py index b2c890a0..a023aab3 100644 --- a/src/connection_manager.py +++ b/src/connection_manager.py @@ -21,6 +21,7 @@ from src.connections.together_connection import TogetherAIConnection from src.connections.evm_connection import EVMConnection from src.connections.perplexity_connection import PerplexityConnection +from src.connections.debridge_connection import DebridgeConnection logger = logging.getLogger("connection_manager") @@ -73,6 +74,8 @@ def _class_name_to_type(class_name: str) -> Type[BaseConnection]: return EVMConnection elif class_name == "perplexity": return PerplexityConnection + elif class_name == "debridge": + return DebridgeConnection return None def _register_connection(self, config_dic: Dict[str, Any]) -> None: From 3f63a399947269857032990e4ca368fbb05dba85 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 11:10:12 +0530 Subject: [PATCH 04/10] fix: replace EthereumConnectionError with EVMConnectionError --- src/connections/evm_connection.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/connections/evm_connection.py b/src/connections/evm_connection.py index 3f6c23a8..aa5fbed2 100644 --- a/src/connections/evm_connection.py +++ b/src/connections/evm_connection.py @@ -56,18 +56,18 @@ def _initialize_web3(self) -> None: self._web3.middleware_onion.inject(geth_poa_middleware, layer=0) if not self._web3.is_connected(): - raise EthereumConnectionError("Failed to connect to Ethereum network") + raise EVMConnectionError("Failed to connect to Ethereum network") chain_id = self._web3.eth.chain_id if chain_id != self.chain_id: - raise EthereumConnectionError(f"Connected to wrong chain. Expected {self.chain_id}, got {chain_id}") + raise EVMConnectionError(f"Connected to wrong chain. Expected {self.chain_id}, got {chain_id}") logger.info(f"Connected to {self.network} network with chain ID: {chain_id}") break except Exception as e: if attempt == 2: - raise EthereumConnectionError(f"Failed to initialize Web3 after 3 attempts: {str(e)}") + raise EVMConnectionError(f"Failed to initialize Web3 after 3 attempts: {str(e)}") logger.warning(f"Web3 initialization attempt {attempt + 1} failed: {str(e)}") time.sleep(1) @@ -497,7 +497,7 @@ def perform_action(self, action_name: str, kwargs: Dict[str, Any]) -> Any: raise KeyError(f"Unknown action: {action_name}") load_dotenv() if not self.is_configured(verbose=True): - raise EthereumConnectionError("Ethereum connection is not properly configured") + raise EVMConnectionError("Ethereum connection is not properly configured") action = self.actions[action_name] errors = action.validate_params(kwargs) if errors: From 067d94ecb5c1bd272ab0d229929ebfab21072aa2 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 16:36:02 +0530 Subject: [PATCH 05/10] feat: connect debridge to the chain connections --- src/actions/debridge_actions.py | 138 +++++++++++++++++++++++ src/connection_manager.py | 5 +- src/connections/debridge_connection.py | 54 ++++++++- src/connections/ethereum_connection.py | 43 +++++++ src/connections/evm_connection.py | 44 ++++++++ src/connections/solana_connection.py | 41 ++++++- src/connections/sonic_connection.py | 43 +++++++ src/helpers/solana/transaction_helper.py | 103 +++++++++++++++++ 8 files changed, 462 insertions(+), 9 deletions(-) create mode 100644 src/actions/debridge_actions.py create mode 100644 src/helpers/solana/transaction_helper.py diff --git a/src/actions/debridge_actions.py b/src/actions/debridge_actions.py new file mode 100644 index 00000000..40b5ce1b --- /dev/null +++ b/src/actions/debridge_actions.py @@ -0,0 +1,138 @@ +import logging +from src.action_handler import register_action + +logger = logging.getLogger("actions.debridge_actions") + +@register_action("create-bridge-tx") +def create_bridge_tx(agent, **kwargs): + """Create Bridge TX using Debridge""" + try: + required_args = ['srcChainId', 'srcChainTokenIn', 'srcChainTokenInAmount', 'dstChainId', 'dstChainTokenOut', 'dstChainTokenOutAmount', 'dstChainTokenOutRecipient', 'srcChainOrderAuthorityAddress', 'dstChainOrderAuthorityAddress'] + for arg in required_args: + if arg not in kwargs: + logger.error(f"Missing required argument: {arg}") + return None + + response = agent.connection_manager.connections["debridge"].create_bridge_tx( + srcChainId=kwargs['srcChainId'], + srcChainTokenIn=kwargs['srcChainTokenIn'], + srcChainTokenInAmount=kwargs['srcChainTokenInAmount'], + dstChainId=kwargs['dstChainId'], + dstChainTokenOut=kwargs['dstChainTokenOut'], + dstChainTokenOutAmount=kwargs['dstChainTokenOutAmount'], + dstChainTokenOutRecipient=kwargs['dstChainTokenOutRecipient'], + srcChainOrderAuthorityAddress=kwargs['srcChainOrderAuthorityAddress'], + dstChainOrderAuthorityAddress=kwargs['dstChainOrderAuthorityAddress'], + affiliateFeeRecipient=kwargs.get('affiliateFeeRecipient'), + prependOperatingExpense=kwargs.get('prependOperatingExpense', True), + affiliateFeePercent=kwargs.get('affiliateFeePercent', 0) + ) + return response + except Exception as e: + logger.error(f"Failed to create bridge transaction: {str(e)}") + return None + +@register_action("get-order-status") +def get_order_status(agent, **kwargs): + """Get order status using Debridge""" + try: + response = agent.connection_manager.connections["debridge"].get_order_status( + id=kwargs.get('id'), + hash=kwargs.get('hash') + ) + return response + except Exception as e: + logger.error(f"Failed to get order status: {str(e)}") + return None + +@register_action("get-order-details") +def get_order_details(agent, **kwargs): + """Get order details using Debridge""" + try: + if 'id' not in kwargs: + logger.error("Missing required argument: id") + return None + + response = agent.connection_manager.connections["debridge"].get_order_details( + id=kwargs['id'] + ) + return response + except Exception as e: + logger.error(f"Failed to get order details: {str(e)}") + return None + +@register_action("cancel-tx") +def cancel_tx(agent, **kwargs): + """Cancel transaction using Debridge""" + try: + if 'id' not in kwargs: + logger.error("Missing required argument: id") + return None + + response = agent.connection_manager.connections["debridge"].cancel_tx( + id=kwargs['id'] + ) + return response + except Exception as e: + logger.error(f"Failed to cancel transaction: {str(e)}") + return None + +@register_action("extcall-cancel-tx") +def extcall_cancel_tx(agent, **kwargs): + """External call to cancel transaction using Debridge""" + try: + if 'id' not in kwargs: + logger.error("Missing required argument: id") + return None + + response = agent.connection_manager.connections["debridge"].extcall_cancel_tx( + id=kwargs['id'] + ) + return response + except Exception as e: + logger.error(f"Failed to perform external call to cancel transaction: {str(e)}") + return None + +@register_action("get-supported-chains") +def get_supported_chains(agent, **kwargs): + """Get supported chains using Debridge""" + try: + response = agent.connection_manager.connections["debridge"].get_supported_chains() + return response + except Exception as e: + logger.error(f"Failed to get supported chains: {str(e)}") + return None + +@register_action("get-token-list") +def get_token_list(agent, **kwargs): + """Get token list using Debridge""" + try: + if 'chainId' not in kwargs: + logger.error("Missing required argument: chainId") + return None + + response = agent.connection_manager.connections["debridge"].get_token_list( + chainId=kwargs['chainId'] + ) + return response + except Exception as e: + logger.error(f"Failed to get token list: {str(e)}") + return None + +@register_action("execute-bridge-tx") +def execute_bridge_tx(agent, **kwargs): + """Execute bridge transaction using Debridge""" + try: + if 'connection' not in kwargs: + logger.error("Missing required argument: connection") + return None + + response = agent.connection_manager.connections["debridge"].execute_bridge_tx( + connection=kwargs['connection'], + compute_unit_price=kwargs.get('compute_unit_price', 200_000), + compute_unit_limit=kwargs.get('compute_unit_limit') + ) + return response + except Exception as e: + logger.error(f"Failed to execute bridge transaction: {str(e)}") + return None diff --git a/src/connection_manager.py b/src/connection_manager.py index a023aab3..74adc45b 100644 --- a/src/connection_manager.py +++ b/src/connection_manager.py @@ -90,7 +90,10 @@ def _register_connection(self, config_dic: Dict[str, Any]) -> None: try: name = config_dic["name"] connection_class = self._class_name_to_type(name) - connection = connection_class(config_dic) + if name == "debridge": + connection = DebridgeConnection(config_dic, self.connections) + else: + connection = connection_class(config_dic) self.connections[name] = connection except Exception as e: logging.error(f"Failed to initialize connection {name}: {e}") diff --git a/src/connections/debridge_connection.py b/src/connections/debridge_connection.py index 367f6c54..4732fbd7 100644 --- a/src/connections/debridge_connection.py +++ b/src/connections/debridge_connection.py @@ -2,6 +2,7 @@ import requests from typing import Dict, Any import os +import json from dotenv import load_dotenv from .base_connection import BaseConnection, Action, ActionParameter @@ -15,10 +16,12 @@ class DebridgeConnectionError(Exception): class DebridgeConnection(BaseConnection): - def __init__(self, config: Dict[str, Any]): + def __init__(self, config: Dict[str, Any], connections: Dict[str, Any]): super().__init__(config) self.api_url = "https://dln.debridge.finance" self._initialize() + self.pending_tx = None + self.connections = connections def _initialize(self): """Initialize Debridge connection""" @@ -67,6 +70,15 @@ def register_actions(self) -> None: required=False, description='Affiliate fee percentage') ] ) + self.actions['execute-bridge-tx'] = Action( + name='execute-bridge-tx', + description='Execute bridge transaction using Debridge', + parameters=[ + ActionParameter(name='connection', type=str, required=True, description='Connection name'), + ActionParameter(name='compute_unit_price', type=int, required=False, description='Compute unit price'), + ActionParameter(name='compute_unit_limit', type=int, required=False, description='Compute unit limit') + ] + ) self.actions['get-order-status'] = Action( name='get-order-status', description='Get order status using Debridge', @@ -121,7 +133,9 @@ def configure(self) -> bool: def is_configured(self, verbose: bool = False) -> bool: """Check if the connection is properly configured""" try: - # TODO: Add any additional checks if needed + if not self.connections: + logger.error("No connections found") + return False return True except Exception as e: if verbose: @@ -137,7 +151,7 @@ def create_bridge_tx( dstChainTokenOutRecipient: str, srcChainOrderAuthorityAddress: str, dstChainOrderAuthorityAddress: str, - affiliateFeeRecipient: str, + affiliateFeeRecipient: str = None, prependOperatingExpense: bool = True, srcChainTokenInAmount: str = "auto", dstChainTokenOutAmount: str = "auto", @@ -163,10 +177,42 @@ def create_bridge_tx( response = requests.get(f"{self.api_url}/v1.0/dln/order/create-tx", params=params) response.raise_for_status() - return response.json() + data = response.json() + self.pending_tx = data + logger.info(json.dumps(data["estimation"], indent=4)) except Exception as e: raise DebridgeConnectionError(f"Failed to bridge assets: {str(e)}") + + def execute_bridge_tx(self, connection: str, compute_unit_price: int = 200_000, compute_unit_limit: int = None) -> None: + """Execute bridge transaction using Debridge""" + try: + logger.info("Executing bridge transaction...") + + if not self.pending_tx: + raise ValueError("No pending transaction found") + + connection_class: BaseConnection = self.connections[connection] + + if connection == "solana": + tx_url = connection_class.perform_action("send-bridge-transaction", { + "tx": self.pending_tx["tx"]["data"], + "compute_unit_price": compute_unit_price, + "compute_unit_limit": compute_unit_limit + }) + else: + tx_url = connection_class.perform_action("send-transaction", { + "tx": json.dumps({ + "tx": self.pending_tx["tx"], + "estimation": self.pending_tx["estimation"] + }) + }) + + self.pending_tx = None + return tx_url + + except Exception as e: + raise DebridgeConnectionError(f"Failed to execute bridge transaction: {str(e)}") def get_order_status(self, id: str = None, hash: str = None) -> Dict: """Get order status using Debridge""" diff --git a/src/connections/ethereum_connection.py b/src/connections/ethereum_connection.py index 8bb1bb63..41308c18 100644 --- a/src/connections/ethereum_connection.py +++ b/src/connections/ethereum_connection.py @@ -2,6 +2,7 @@ import os import time import requests +import json from typing import Dict, Any, Optional, Union from dotenv import load_dotenv, set_key from web3 import Web3 @@ -12,6 +13,8 @@ logger = logging.getLogger("connections.ethereum_connection") +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" + class EthereumConnectionError(Exception): """Base exception for Ethereum connection errors""" pass @@ -116,6 +119,13 @@ def register_actions(self) -> None: ActionParameter("slippage", False, float, "Max slippage percentage (default 0.5%)") ], description="Swap tokens using Kyberswap aggregator" + ), + "send-transaction": Action( + name="send-transaction", + parameters=[ + ActionParameter("tx", True, str, "Raw transaction data") + ], + description="Send a raw transaction to the Ethereum network" ) } @@ -627,6 +637,39 @@ def swap( except Exception as e: return f"Swap failed: {str(e)}" + + def send_transaction(self, tx: Dict[str, Any]) -> str: + """Send a raw transaction to the Ethereum network""" + try: + private_key = os.getenv('ETH_PRIVATE_KEY') + account = self._web3.eth.account.from_key(private_key) + + data = json.loads(tx) + tx = data["tx"] + tx["from"] = account.address + tx["nonce"] = self._web3.eth.get_transaction_count(account.address) + tx["gasPrice"] = self._web3.eth.gas_price + tx["chainId"] = self._web3.eth.chain_id + + if data["estimation"]["srcChainTokenIn"]["address"] != ZERO_ADDRESS: + self._handle_token_approval( + data["estimation"]["srcChainTokenIn"]["address"], + tx["to"], + int(data["estimation"]["srcChainTokenIn"]["amount"]) + ) + + if "value" in tx: + tx["value"] = int(tx["value"]) + try: + tx['gas'] = self._web3.eth.estimate_gas(tx) + except Exception as e: + raise ValueError(f"Gas estimation failed: {e}") + signed_tx = account.sign_transaction(tx) + tx_hash = self._web3.eth.send_raw_transaction(signed_tx.rawTransaction) + tx_link = self._get_explorer_link(tx_hash.hex()) + return tx_link + except Exception as e: + return f"Transaction failed: {str(e)}" def perform_action(self, action_name: str, kwargs: Dict[str, Any]) -> Any: """Execute an Ethereum action with validation""" diff --git a/src/connections/evm_connection.py b/src/connections/evm_connection.py index aa5fbed2..2f8694e2 100644 --- a/src/connections/evm_connection.py +++ b/src/connections/evm_connection.py @@ -2,6 +2,7 @@ import os import time import requests +import json from typing import Dict, Any, Optional, Union from dotenv import load_dotenv, set_key from web3 import Web3 @@ -12,6 +13,7 @@ logger = logging.getLogger("connections.evm_connection") +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" class EVMConnectionError(Exception): """Base exception for EVM connection errors""" @@ -123,6 +125,13 @@ def register_actions(self) -> None: ActionParameter("slippage", False, float, "Max slippage percentage (default 0.5%)") ], description="Swap tokens using Kyberswap aggregator" + ), + "send-transaction": Action( + name="send-transaction", + parameters=[ + ActionParameter("tx", True, str, "Transaction object") + ], + description="Send a signed transaction" ) } @@ -490,6 +499,41 @@ def swap(self, token_in: str, token_out: str, amount: float, slippage: float = 0 except Exception as e: return f"Swap failed: {str(e)}" + + def send_transaction(self, tx: str) -> str: + """Send a signed transaction""" + try: + private_key = os.getenv('EVM_PRIVATE_KEY') or os.getenv('ETH_PRIVATE_KEY') + account = self._web3.eth.account.from_key(private_key) + + data = json.loads(tx) + tx = data["tx"] + tx["from"] = account.address + tx["nonce"] = self._web3.eth.get_transaction_count(account.address) + tx["gasPrice"] = self._web3.eth.gas_price + tx["chainId"] = self._web3.eth.chain_id + + if data["estimation"]["srcChainTokenIn"]["address"] != ZERO_ADDRESS: + self._handle_token_approval( + data["estimation"]["srcChainTokenIn"]["address"], + tx["to"], + int(data["estimation"]["srcChainTokenIn"]["amount"]) + ) + + if "value" in tx: + tx["value"] = int(tx["value"]) + try: + tx['gas'] = self._web3.eth.estimate_gas(tx) + except Exception as e: + raise ValueError(f"Gas estimation failed: {e}") + signed_tx = account.sign_transaction(tx) + tx_hash = self._web3.eth.send_raw_transaction(signed_tx.rawTransaction) + tx_link = self._get_explorer_link(tx_hash.hex()) + return tx_link + + except Exception as e: + logger.error(f"Failed to send transaction: {str(e)}") + raise def perform_action(self, action_name: str, kwargs: Dict[str, Any]) -> Any: """Execute an Ethereum action with validation""" diff --git a/src/connections/solana_connection.py b/src/connections/solana_connection.py index 3635104f..2b498bbc 100644 --- a/src/connections/solana_connection.py +++ b/src/connections/solana_connection.py @@ -1,7 +1,7 @@ import logging import os -import requests import asyncio + from typing import Dict, Any, Optional from src.connections.base_connection import BaseConnection, Action, ActionParameter @@ -16,6 +16,7 @@ from src.helpers.solana.performance import SolanaPerformanceTracker from src.helpers.solana.transfer import SolanaTransferHelper from src.helpers.solana.read import SolanaReadHelper +from src.helpers.solana.transaction_helper import TransactionHelper from dotenv import load_dotenv, set_key @@ -23,10 +24,9 @@ from jupiter_python_sdk.jupiter import Jupiter from solana.rpc.async_api import AsyncClient -from solana.rpc.commitment import Confirmed - -from solders.keypair import Keypair # type: ignore +from solders.keypair import Keypair +from solders.transaction import VersionedTransaction logger = logging.getLogger("connections.solana_connection") @@ -216,6 +216,15 @@ def register_actions(self) -> None: ], description="Launch a Pump & Fun token", ), + "send-bridge-transaction": Action( + name="send-bridge-transaction", + parameters=[ + ActionParameter("tx", True, str, "Hex-encoded transaction"), + ActionParameter("compute_unit_price", True, int, "Compute unit price"), + ActionParameter("compute_unit_limit", False, int, "Compute unit limit"), + ], + description="Send a bridge transaction", + ), } def configure(self) -> bool: @@ -414,6 +423,30 @@ def launch_pump_token( # f"Launched Pump & Fun token {token_ticker}\nToken Mint: {res['mint']}" # ) # return res + + """WARNING: PARTIALLY TESTED""" + def send_bridge_transaction(self, tx: str, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + # Clean the input + tx = tx.strip().replace(" ", "").replace("\n", "").lstrip("0x0") + + # Ensure hex string has even length + if len(tx) % 2 != 0: + tx = "0" + tx + try: + tx_bytes = bytes.fromhex(tx) # Convert to bytes + transaction = VersionedTransaction.from_bytes(tx_bytes) + except ValueError as e: + raise ValueError(f"Hex decoding error: {e}") + + res = self._send_bridge_transaction(transaction, compute_unit_price, compute_unit_limit) + res = asyncio.run(res) + return res + + async def _send_bridge_transaction(self, transaction: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + async_client = self._get_connection_async() + wallet = self._get_wallet() + + return await TransactionHelper.send_bridge_transaction(async_client, wallet, transaction, compute_unit_price, compute_unit_limit) def perform_action(self, action_name: str, kwargs) -> Any: """Execute a Solana action with validation""" diff --git a/src/connections/sonic_connection.py b/src/connections/sonic_connection.py index bc7ab527..294ff742 100644 --- a/src/connections/sonic_connection.py +++ b/src/connections/sonic_connection.py @@ -2,6 +2,7 @@ import os import requests import time +import json from typing import Dict, Any, Optional from dotenv import load_dotenv, set_key from web3 import Web3 @@ -12,6 +13,7 @@ logger = logging.getLogger("connections.sonic_connection") +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" class SonicConnectionError(Exception): """Base exception for Sonic connection errors""" @@ -141,6 +143,13 @@ def register_actions(self) -> None: ActionParameter("slippage", False, float, "Max slippage percentage") ], description="Swap tokens" + ), + "send-transaction": Action( + name="send-transaction", + parameters=[ + ActionParameter("tx", True, str, "Raw transaction data") + ], + description="Send a raw transaction" ) } @@ -438,6 +447,40 @@ def swap(self, token_in: str, token_out: str, amount: float, slippage: float = 0 except Exception as e: logger.error(f"Swap failed: {e}") raise + def send_transaction(self, tx: str) -> str: + """Send a raw transaction to the network""" + try: + private_key = os.getenv('SONIC_PRIVATE_KEY') + account = self._web3.eth.account.from_key(private_key) + + data = json.loads(tx) + tx = data["tx"] + tx["from"] = account.address + tx["nonce"] = self._web3.eth.get_transaction_count(account.address) + tx["gasPrice"] = self._web3.eth.gas_price + tx["chainId"] = self._web3.eth.chain_id + + if data["estimation"]["srcChainTokenIn"]["address"] != ZERO_ADDRESS: + self._handle_token_approval( + data["estimation"]["srcChainTokenIn"]["address"], + tx["to"], + int(data["estimation"]["srcChainTokenIn"]["amount"]) + ) + + if "value" in tx: + tx["value"] = int(tx["value"]) + try: + tx['gas'] = self._web3.eth.estimate_gas(tx) + except Exception as e: + raise ValueError(f"Gas estimation failed: {e}") + signed_tx = account.sign_transaction(tx) + tx_hash = self._web3.eth.send_raw_transaction(signed_tx.rawTransaction) + tx_link = self._get_explorer_link(tx_hash.hex()) + return tx_link + except Exception as e: + logger.error(f"Failed to send transaction: {e}") + raise + def perform_action(self, action_name: str, kwargs) -> Any: """Execute a Sonic action with validation""" if action_name not in self.actions: diff --git a/src/helpers/solana/transaction_helper.py b/src/helpers/solana/transaction_helper.py new file mode 100644 index 00000000..d58ce94d --- /dev/null +++ b/src/helpers/solana/transaction_helper.py @@ -0,0 +1,103 @@ +from typing import Optional +from solders.transaction import VersionedTransaction +from solders.instruction import CompiledInstruction +from solders.keypair import Keypair +from solders.rpc.responses import GetLatestBlockhashResp +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Confirmed, Processed +from solana.rpc.types import TxOpts +import solders.message +import json +import logging + +logger = logging.getLogger("helpers.transaction_helper") + +class TransactionHelper: + @staticmethod + def encode_number_to_array_le(num: int, array_size: int) -> bytearray: + result = bytearray(array_size) + for i in range(array_size): + result[i] = num & 0xFF + num >>= 8 + return result + + @staticmethod + def update_priority_fee(tx: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None): + compute_budget_offset = 1 + + # Modify compute unit price + compute_unit_price_data = bytearray(tx.message.instructions[1].data) + encoded_price = TransactionHelper.encode_number_to_array_le(compute_unit_price, 8) + + for i in range(len(encoded_price)): + compute_unit_price_data[i + compute_budget_offset] = encoded_price[i] + + # Replace instruction with modified data + tx.message.instructions[1] = CompiledInstruction( + program_id_index=tx.message.instructions[1].program_id_index, + accounts=tx.message.instructions[1].accounts, + data=bytes(compute_unit_price_data) + ) + + if compute_unit_limit: + # Modify compute unit limit + compute_unit_limit_data = bytearray(tx.message.instructions[0].data) + encoded_limit = TransactionHelper.encode_number_to_array_le(compute_unit_limit, 4) + + for i in range(len(encoded_limit)): + compute_unit_limit_data[i + compute_budget_offset] = encoded_limit[i] + + # Replace instruction with modified data + tx.message.instructions[0] = CompiledInstruction( + program_id_index=tx.message.instructions[0].program_id_index, + accounts=tx.message.instructions[0].accounts, + data=bytes(compute_unit_limit_data) + ) + + @staticmethod + async def send_bridge_transaction(async_client: AsyncClient, wallet: Keypair, transaction: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + try: + # Update priority fee + TransactionHelper.update_priority_fee(transaction, compute_unit_price, compute_unit_limit) + + # Fetch latest blockhash + latest_blockhash_resp: GetLatestBlockhashResp = await async_client.get_latest_blockhash(Confirmed) + latest_blockhash = latest_blockhash_resp.value.blockhash + + # Create message with updated blockhash + updated_message = solders.message.MessageV0( + header=transaction.message.header, + account_keys=transaction.message.account_keys, + recent_blockhash=latest_blockhash, + instructions=transaction.message.instructions, + address_table_lookups=transaction.message.address_table_lookups + ) + + # Sign the message + signature = wallet.sign_message( + solders.message.to_bytes_versioned(updated_message) + ) + logger.info(signature) + + # Create signed transaction with single signature + signed_tx = VersionedTransaction.populate( + updated_message, [signature] + ) + + # Send the transaction + opts = TxOpts(skip_preflight=False, preflight_commitment=Processed) + result = await async_client.send_raw_transaction( + txn=bytes(signed_tx), + opts=opts + ) + + # Log and return transaction ID + transaction_id = json.loads(result.to_json())["result"] + logger.debug(f"Transaction sent: https://explorer.solana.com/tx/{transaction_id}") + + # Use signature for confirmation + await async_client.confirm_transaction(signature, commitment=Confirmed) + return f"https://explorer.solana.com/tx/{transaction_id}" + + except Exception as e: + raise ValueError(f"Error sending transaction: {e}") From 71b893d3cc5e2509c66ffd46da407e23fdda3ac2 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 16:43:46 +0530 Subject: [PATCH 06/10] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80fe8b54..f1828ca5 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ Each plugin has its own configuration options that can be specified in the agent - Custom slippage settings - Token swaps via Sonic DEX - Network switching (mainnet/testnet) - + - DeBridge - Bridge tokens across multiple chains - EternalAI - Transform agents to smart contracts - Deploy on 10+ blockchains From c48a0c197cbb777d92f1e0484172dc040b346a48 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 16:49:39 +0530 Subject: [PATCH 07/10] feat: add dlnHook parameter --- src/connections/debridge_connection.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/connections/debridge_connection.py b/src/connections/debridge_connection.py index 4732fbd7..0418e23e 100644 --- a/src/connections/debridge_connection.py +++ b/src/connections/debridge_connection.py @@ -66,6 +66,8 @@ def register_actions(self) -> None: description='Affiliate fee recipient address'), ActionParameter(name='prependOperatingExpense', type=bool, required=False, description='Prepend operating expense'), + ActionParameter(name='dlnHook', type=str, required=False, + description='DLN hook'), ActionParameter(name='affiliateFeePercent', type=float, required=False, description='Affiliate fee percentage') ] @@ -155,7 +157,8 @@ def create_bridge_tx( prependOperatingExpense: bool = True, srcChainTokenInAmount: str = "auto", dstChainTokenOutAmount: str = "auto", - affiliateFeePercent: float = 0 + affiliateFeePercent: float = 0, + dlnHook: str = None ) -> Dict: """Create Bridge TX using Debridge""" try: @@ -172,6 +175,7 @@ def create_bridge_tx( "dstChainOrderAuthorityAddress": dstChainOrderAuthorityAddress, "affiliateFeePercent": affiliateFeePercent, "affiliateFeeRecipient": affiliateFeeRecipient, + "dlnHook": dlnHook, "accesstoken": self.access_key } From a7681974bcf9de8024f86cd81889b0ffa9bd81cc Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 18:47:52 +0530 Subject: [PATCH 08/10] minor changes --- src/connections/debridge_connection.py | 15 ++++++++++----- src/connections/solana_connection.py | 15 ++++++++------- src/helpers/solana/transaction_helper.py | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/connections/debridge_connection.py b/src/connections/debridge_connection.py index 0418e23e..34302445 100644 --- a/src/connections/debridge_connection.py +++ b/src/connections/debridge_connection.py @@ -176,7 +176,10 @@ def create_bridge_tx( "affiliateFeePercent": affiliateFeePercent, "affiliateFeeRecipient": affiliateFeeRecipient, "dlnHook": dlnHook, - "accesstoken": self.access_key + "accesstoken": self.access_key, + "referralCode": 21064, + "deBridgeApp": "ZEREPY" + } response = requests.get(f"{self.api_url}/v1.0/dln/order/create-tx", params=params) @@ -199,11 +202,13 @@ def execute_bridge_tx(self, connection: str, compute_unit_price: int = 200_000, connection_class: BaseConnection = self.connections[connection] if connection == "solana": - tx_url = connection_class.perform_action("send-bridge-transaction", { + data = { "tx": self.pending_tx["tx"]["data"], - "compute_unit_price": compute_unit_price, - "compute_unit_limit": compute_unit_limit - }) + "compute_unit_price": compute_unit_price + } + if compute_unit_limit: + data["compute_unit_limit"] = compute_unit_limit + tx_url = connection_class.perform_action("send-transaction", data) else: tx_url = connection_class.perform_action("send-transaction", { "tx": json.dumps({ diff --git a/src/connections/solana_connection.py b/src/connections/solana_connection.py index 2b498bbc..6bec95f5 100644 --- a/src/connections/solana_connection.py +++ b/src/connections/solana_connection.py @@ -216,14 +216,14 @@ def register_actions(self) -> None: ], description="Launch a Pump & Fun token", ), - "send-bridge-transaction": Action( - name="send-bridge-transaction", + "send-transaction": Action( + name="send-transaction", parameters=[ ActionParameter("tx", True, str, "Hex-encoded transaction"), ActionParameter("compute_unit_price", True, int, "Compute unit price"), ActionParameter("compute_unit_limit", False, int, "Compute unit limit"), ], - description="Send a bridge transaction", + description="Send a hex-encoded transaction transaction", ), } @@ -425,7 +425,8 @@ def launch_pump_token( # return res """WARNING: PARTIALLY TESTED""" - def send_bridge_transaction(self, tx: str, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + def send_transaction(self, tx: str, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + """Send a hex-encoded transaction""" # Clean the input tx = tx.strip().replace(" ", "").replace("\n", "").lstrip("0x0") @@ -438,15 +439,15 @@ def send_bridge_transaction(self, tx: str, compute_unit_price: int, compute_unit except ValueError as e: raise ValueError(f"Hex decoding error: {e}") - res = self._send_bridge_transaction(transaction, compute_unit_price, compute_unit_limit) + res = self._send_transaction(transaction, compute_unit_price, compute_unit_limit) res = asyncio.run(res) return res - async def _send_bridge_transaction(self, transaction: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + async def _send_transaction(self, transaction: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: async_client = self._get_connection_async() wallet = self._get_wallet() - return await TransactionHelper.send_bridge_transaction(async_client, wallet, transaction, compute_unit_price, compute_unit_limit) + return await TransactionHelper.send_transaction(async_client, wallet, transaction, compute_unit_price, compute_unit_limit) def perform_action(self, action_name: str, kwargs) -> Any: """Execute a Solana action with validation""" diff --git a/src/helpers/solana/transaction_helper.py b/src/helpers/solana/transaction_helper.py index d58ce94d..0a8bc77c 100644 --- a/src/helpers/solana/transaction_helper.py +++ b/src/helpers/solana/transaction_helper.py @@ -55,7 +55,7 @@ def update_priority_fee(tx: VersionedTransaction, compute_unit_price: int, compu ) @staticmethod - async def send_bridge_transaction(async_client: AsyncClient, wallet: Keypair, transaction: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: + async def send_transaction(async_client: AsyncClient, wallet: Keypair, transaction: VersionedTransaction, compute_unit_price: int, compute_unit_limit: Optional[int] = None) -> str: try: # Update priority fee TransactionHelper.update_priority_fee(transaction, compute_unit_price, compute_unit_limit) From 1fdcd5aadfc37b36ca91ac3ef54285d8e2639766 Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 20:16:37 +0530 Subject: [PATCH 09/10] reduce required params --- src/actions/debridge_actions.py | 11 ++++++----- src/connections/debridge_connection.py | 16 +++++++++------- src/connections/solana_connection.py | 3 +++ src/connections/sonic_connection.py | 8 ++++++++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/actions/debridge_actions.py b/src/actions/debridge_actions.py index 40b5ce1b..1529c9aa 100644 --- a/src/actions/debridge_actions.py +++ b/src/actions/debridge_actions.py @@ -7,25 +7,26 @@ def create_bridge_tx(agent, **kwargs): """Create Bridge TX using Debridge""" try: - required_args = ['srcChainId', 'srcChainTokenIn', 'srcChainTokenInAmount', 'dstChainId', 'dstChainTokenOut', 'dstChainTokenOutAmount', 'dstChainTokenOutRecipient', 'srcChainOrderAuthorityAddress', 'dstChainOrderAuthorityAddress'] + required_args = ['connection', 'srcChainId', 'srcChainTokenIn', 'srcChainTokenInAmount', 'dstChainId', 'dstChainTokenOut', 'dstChainTokenOutRecipient'] for arg in required_args: if arg not in kwargs: logger.error(f"Missing required argument: {arg}") return None response = agent.connection_manager.connections["debridge"].create_bridge_tx( + connection=kwargs['connection'], srcChainId=kwargs['srcChainId'], srcChainTokenIn=kwargs['srcChainTokenIn'], srcChainTokenInAmount=kwargs['srcChainTokenInAmount'], dstChainId=kwargs['dstChainId'], dstChainTokenOut=kwargs['dstChainTokenOut'], - dstChainTokenOutAmount=kwargs['dstChainTokenOutAmount'], dstChainTokenOutRecipient=kwargs['dstChainTokenOutRecipient'], - srcChainOrderAuthorityAddress=kwargs['srcChainOrderAuthorityAddress'], - dstChainOrderAuthorityAddress=kwargs['dstChainOrderAuthorityAddress'], + dstChainTokenOutAmount=kwargs.get('dstChainTokenOutAmount', "auto"), + dstChainOrderAuthorityAddress=kwargs.get('dstChainOrderAuthorityAddress'), affiliateFeeRecipient=kwargs.get('affiliateFeeRecipient'), prependOperatingExpense=kwargs.get('prependOperatingExpense', True), - affiliateFeePercent=kwargs.get('affiliateFeePercent', 0) + affiliateFeePercent=kwargs.get('affiliateFeePercent', 0), + dlnHook=kwargs.get('dlnHook') ) return response except Exception as e: diff --git a/src/connections/debridge_connection.py b/src/connections/debridge_connection.py index 34302445..b0a078ad 100644 --- a/src/connections/debridge_connection.py +++ b/src/connections/debridge_connection.py @@ -44,6 +44,7 @@ def register_actions(self) -> None: name='create-bridge-tx', description='Create Bridge TX using Debridge', parameters=[ + ActionParameter(name='connection', type=str, required=True, description='Connection name'), ActionParameter(name='srcChainId', type=int, required=True, description='Source chain ID'), ActionParameter(name='srcChainTokenIn', type=str, @@ -54,13 +55,11 @@ def register_actions(self) -> None: required=True, description='Destination chain ID'), ActionParameter(name='dstChainTokenOut', type=str, required=True, description='Destination chain token address'), - ActionParameter(name='dstChainTokenOutAmount', type=str, - required=True, description='Amount of destination chain token'), ActionParameter(name='dstChainTokenOutRecipient', type=str, required=True, description='Recipient address on destination chain'), - ActionParameter(name='srcChainOrderAuthorityAddress', type=str, required=True, - description='Source chain order authority address'), - ActionParameter(name='dstChainOrderAuthorityAddress', type=str, required=True, + ActionParameter(name='dstChainTokenOutAmount', type=str, + required=False, description='Amount of destination chain token'), + ActionParameter(name='dstChainOrderAuthorityAddress', type=str, required=False, description='Destination chain order authority address'), ActionParameter(name='affiliateFeeRecipient', type=str, required=False, description='Affiliate fee recipient address'), @@ -146,13 +145,13 @@ def is_configured(self, verbose: bool = False) -> bool: def create_bridge_tx( self, + connection: str, srcChainId: int, srcChainTokenIn: str, dstChainId: int, dstChainTokenOut: str, dstChainTokenOutRecipient: str, - srcChainOrderAuthorityAddress: str, - dstChainOrderAuthorityAddress: str, + dstChainOrderAuthorityAddress: str = None, affiliateFeeRecipient: str = None, prependOperatingExpense: bool = True, srcChainTokenInAmount: str = "auto", @@ -162,6 +161,9 @@ def create_bridge_tx( ) -> Dict: """Create Bridge TX using Debridge""" try: + connection_class: BaseConnection = self.connections[connection] + srcChainOrderAuthorityAddress = connection_class.get_address().split(" ")[-1] + logger.info(srcChainOrderAuthorityAddress) params = { "srcChainId": srcChainId, "srcChainTokenIn": srcChainTokenIn, diff --git a/src/connections/solana_connection.py b/src/connections/solana_connection.py index 6bec95f5..717e8451 100644 --- a/src/connections/solana_connection.py +++ b/src/connections/solana_connection.py @@ -292,6 +292,9 @@ def is_configured(self, verbose: bool = False) -> bool: logger.debug(f"Solana Configuration validation failed: {error_msg}") return False + def get_address(self) -> str: + return str(self._get_wallet().pubkey()) + def transfer( self, to_address: str, amount: float, token_mint: Optional[str] = None ) -> str: diff --git a/src/connections/sonic_connection.py b/src/connections/sonic_connection.py index 294ff742..5f42bca6 100644 --- a/src/connections/sonic_connection.py +++ b/src/connections/sonic_connection.py @@ -58,6 +58,14 @@ def _initialize_web3(self): except Exception as e: logger.warning(f"Could not get chain ID: {e}") + def get_address(self) -> str: + try: + private_key = os.getenv('SONIC_PRIVATE_KEY') + account = self._web3.eth.account.from_key(private_key) + return f"Your Sonic address: {account.address}" + except Exception as e: + return f"Failed to get address: {str(e)}" + @property def is_llm_provider(self) -> bool: return False From 7348f144c514ead0e52da9727167edb5e304cbfc Mon Sep 17 00:00:00 2001 From: Parth Gupta Date: Sat, 22 Feb 2025 20:44:23 +0530 Subject: [PATCH 10/10] fix: handle token approvals correctly --- src/connections/debridge_connection.py | 12 ++++++++++++ src/connections/ethereum_connection.py | 9 +-------- src/connections/evm_connection.py | 7 ------- src/connections/sonic_connection.py | 9 +-------- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/connections/debridge_connection.py b/src/connections/debridge_connection.py index b0a078ad..389cfc3f 100644 --- a/src/connections/debridge_connection.py +++ b/src/connections/debridge_connection.py @@ -212,6 +212,18 @@ def execute_bridge_tx(self, connection: str, compute_unit_price: int = 200_000, data["compute_unit_limit"] = compute_unit_limit tx_url = connection_class.perform_action("send-transaction", data) else: + if self.pending_tx["tx"]["allowanceTarget"]: + logger.info("The bridge transaction requires approval. \nApproving token...") + + connection_class._handle_token_approval( + token_address=self.pending_tx["estimation"]["srcChainTokenIn"]["address"], + spender_address=self.pending_tx["tx"]["allowanceTarget"], + amount=int(self.pending_tx["tx"]["allowanceValue"]) + ) + logger.info("Token approved. \nPlease create a new transaction.") + + self.pending_tx = None + return tx_url = connection_class.perform_action("send-transaction", { "tx": json.dumps({ "tx": self.pending_tx["tx"], diff --git a/src/connections/ethereum_connection.py b/src/connections/ethereum_connection.py index 41308c18..73a56382 100644 --- a/src/connections/ethereum_connection.py +++ b/src/connections/ethereum_connection.py @@ -650,14 +650,7 @@ def send_transaction(self, tx: Dict[str, Any]) -> str: tx["nonce"] = self._web3.eth.get_transaction_count(account.address) tx["gasPrice"] = self._web3.eth.gas_price tx["chainId"] = self._web3.eth.chain_id - - if data["estimation"]["srcChainTokenIn"]["address"] != ZERO_ADDRESS: - self._handle_token_approval( - data["estimation"]["srcChainTokenIn"]["address"], - tx["to"], - int(data["estimation"]["srcChainTokenIn"]["amount"]) - ) - + if "value" in tx: tx["value"] = int(tx["value"]) try: diff --git a/src/connections/evm_connection.py b/src/connections/evm_connection.py index 2f8694e2..8e41e6b0 100644 --- a/src/connections/evm_connection.py +++ b/src/connections/evm_connection.py @@ -513,13 +513,6 @@ def send_transaction(self, tx: str) -> str: tx["gasPrice"] = self._web3.eth.gas_price tx["chainId"] = self._web3.eth.chain_id - if data["estimation"]["srcChainTokenIn"]["address"] != ZERO_ADDRESS: - self._handle_token_approval( - data["estimation"]["srcChainTokenIn"]["address"], - tx["to"], - int(data["estimation"]["srcChainTokenIn"]["amount"]) - ) - if "value" in tx: tx["value"] = int(tx["value"]) try: diff --git a/src/connections/sonic_connection.py b/src/connections/sonic_connection.py index 5f42bca6..b6ddcbfb 100644 --- a/src/connections/sonic_connection.py +++ b/src/connections/sonic_connection.py @@ -467,14 +467,7 @@ def send_transaction(self, tx: str) -> str: tx["nonce"] = self._web3.eth.get_transaction_count(account.address) tx["gasPrice"] = self._web3.eth.gas_price tx["chainId"] = self._web3.eth.chain_id - - if data["estimation"]["srcChainTokenIn"]["address"] != ZERO_ADDRESS: - self._handle_token_approval( - data["estimation"]["srcChainTokenIn"]["address"], - tx["to"], - int(data["estimation"]["srcChainTokenIn"]["amount"]) - ) - + if "value" in tx: tx["value"] = int(tx["value"]) try: