From e34c2b89412d7e33b24295bafefec812598d762b Mon Sep 17 00:00:00 2001 From: aitbc Date: Thu, 7 May 2026 22:16:45 +0200 Subject: [PATCH] Fix complete_atomic_swap to use CLI - Removed Web3 dependency from complete_atomic_swap - Uses CLI via send_transaction() now - Converts swap_id and secret to bytes before sending - All atomic swap methods now work without Web3 --- .../src/aitbc_agent/cli_contract_client.py | 3 +- .../src/aitbc_agent/contract_integration.py | 68 ++++++------- .../tests/test_cli_atomic_swap.py | 99 +++++++++++++++++++ 3 files changed, 132 insertions(+), 38 deletions(-) create mode 100644 packages/py/aitbc-agent-sdk/tests/test_cli_atomic_swap.py diff --git a/packages/py/aitbc-agent-sdk/src/aitbc_agent/cli_contract_client.py b/packages/py/aitbc-agent-sdk/src/aitbc_agent/cli_contract_client.py index 09828d22..238dc63e 100644 --- a/packages/py/aitbc-agent-sdk/src/aitbc_agent/cli_contract_client.py +++ b/packages/py/aitbc-agent-sdk/src/aitbc_agent/cli_contract_client.py @@ -6,9 +6,10 @@ import subprocess import asyncio import os import json +import logging from typing import Dict, Any, Optional -logger = None # Will be set later +logger = logging.getLogger(__name__) def set_logger(log): global logger diff --git a/packages/py/aitbc-agent-sdk/src/aitbc_agent/contract_integration.py b/packages/py/aitbc-agent-sdk/src/aitbc_agent/contract_integration.py index fe213f07..730373a3 100644 --- a/packages/py/aitbc-agent-sdk/src/aitbc_agent/contract_integration.py +++ b/packages/py/aitbc-agent-sdk/src/aitbc_agent/contract_integration.py @@ -7,8 +7,16 @@ import asyncio import json from typing import Dict, List, Optional, Any, Union, Callable from dataclasses import dataclass -from web3 import Web3 -from web3.contract import Contract + +# Optional Web3 import for Web3-based client +try: + from web3 import Web3 + from web3.contract import Contract + WEB3_AVAILABLE = True +except ImportError: + WEB3_AVAILABLE = False + Web3 = None + Contract = None from aitbc.aitbc_logging import get_logger from aitbc.exceptions import NetworkError @@ -54,6 +62,8 @@ class ContractClient: """Web3 client for smart contract interactions""" def __init__(self, config: ContractConfig, private_key: Optional[str] = None): + if not WEB3_AVAILABLE: + raise ImportError("Web3 is required for ContractClient. Use CLIContractClient instead.") self.config = config self.private_key = private_key self.w3: Optional[Web3] = None @@ -102,7 +112,6 @@ class ContractClient: abi=staking_contract_abi ) - # Load CrossChainAtomicSwap contract if self.config.cross_chain_atomic_swap: self.contracts["cross_chain_atomic_swap"] = self.w3.eth.contract( address=self.config.cross_chain_atomic_swap, @@ -450,45 +459,30 @@ class AgentContractIntegration: secret: str, contract_address: str ) -> Dict[str, Any]: - """Complete atomic swap by revealing secret""" + """Complete atomic swap by revealing secret via CLI""" try: - # Load the atomic swap contract - atomic_swap_abi = self._load_abi("CrossChainAtomicSwap") - atomic_swap_contract = self.contract_client.w3.eth.contract( - address=contract_address, - abi=atomic_swap_abi + swap_id_bytes = bytes.fromhex(swap_id) + secret_bytes = bytes.fromhex(secret) + + tx_hash = await self.contract_client.send_transaction( + "cross_chain_atomic_swap", + "completeSwap", + swap_id_bytes, + secret_bytes ) - # Build and send transaction - transaction = atomic_swap_contract.functions.completeSwap( - swap_id, - secret - ).build_transaction({ - 'from': self.contract_client.w3.eth.account.from_key(self.contract_client.private_key).address, - 'gas': 200000, - 'gasPrice': self.contract_client.w3.eth.gas_price, - 'nonce': self.contract_client.w3.eth.get_transaction_count( - self.contract_client.w3.eth.account.from_key(self.contract_client.private_key).address - ), - }) - - # Sign transaction - signed_txn = self.contract_client.w3.eth.account.sign_transaction(transaction, self.contract_client.private_key) - - # Send transaction - tx_hash = self.contract_client.w3.eth.send_raw_transaction(signed_txn.rawTransaction) - - logger.info(f"Atomic swap completed: {tx_hash.hex()}") - - # Wait for confirmation receipt = await self.contract_client.wait_for_transaction(tx_hash) - return { - "swap_id": swap_id, - "tx_hash": tx_hash.hex(), - "status": "COMPLETED" if receipt["status"] == 1 else "FAILED", - "block_number": receipt["blockNumber"] - } + if receipt["status"] == "success": + logger.info(f"Atomic swap completed: {swap_id}") + return { + "swap_id": swap_id, + "tx_hash": tx_hash, + "status": "COMPLETED", + "block_number": receipt.get("block_number", 0) + } + else: + raise Exception(f"Transaction failed: {receipt}") except Exception as e: logger.error(f"Failed to complete atomic swap: {e}") diff --git a/packages/py/aitbc-agent-sdk/tests/test_cli_atomic_swap.py b/packages/py/aitbc-agent-sdk/tests/test_cli_atomic_swap.py new file mode 100644 index 00000000..ea25f176 --- /dev/null +++ b/packages/py/aitbc-agent-sdk/tests/test_cli_atomic_swap.py @@ -0,0 +1,99 @@ +""" +Test atomic swap methods with CLI client +""" +import asyncio +import sys +import os +import logging + +# Setup logging +logging.basicConfig(level=logging.INFO) + +# Add the src directory to the path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +# Mock aitbc.aitbc_logging +class MockLogger: + @staticmethod + def get_logger(name): + return logging.getLogger(name) + +sys.modules['aitbc'] = type(sys)('aitbc') +sys.modules['aitbc.aitbc_logging'] = MockLogger +sys.modules['aitbc.exceptions'] = type(sys)('aitbc.exceptions') +sys.modules['aitbc.exceptions'].NetworkError = Exception + +from aitbc_agent.contract_integration import ContractConfig, create_agent_contract_integration + +async def test_cli_client(): + """Test CLI client atomic swap methods""" + print("Testing CLI client atomic swap methods...") + + # Create config with use_cli=True + config = ContractConfig( + payment_processor="0xpaymentprocessor", + agent_marketplace="0xagentmarketplace", + staking_contract="0xstakingcontract", + treasury_manager="0xtreasurymanager", + cross_chain_atomic_swap="0xcrosschainatomicswap_1778182201", + use_cli=True, + rpc_url="http://localhost:8545" + ) + + # Create integration with CLI client + integration = create_agent_contract_integration(config, private_key=None) + + print(f"Contract client type: {type(integration.contract_client).__name__}") + print(f"CLI available: {integration.contract_client.__class__.__name__ == 'CLIContractClient'}") + + # Test initiate_atomic_swap + try: + swap_id = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + hashlock = "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" + result = await integration.initiate_atomic_swap( + swap_id=swap_id, + token="AITBC", + amount=1000, + participant="test_participant", + hashlock=hashlock, + timelock=3600, + contract_address=config.cross_chain_atomic_swap + ) + print(f"initiate_atomic_swap result: {result}") + except Exception as e: + print(f"initiate_atomic_swap error: {e}") + + # Test complete_atomic_swap + try: + secret = "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321" + result = await integration.complete_atomic_swap( + swap_id=swap_id, + secret=secret, + contract_address=config.cross_chain_atomic_swap + ) + print(f"complete_atomic_swap result: {result}") + except Exception as e: + print(f"complete_atomic_swap error: {e}") + + # Test get_swap_status + try: + result = await integration.get_swap_status( + swap_id=swap_id, + contract_address=config.cross_chain_atomic_swap + ) + print(f"get_swap_status result: {result}") + except Exception as e: + print(f"get_swap_status error: {e}") + + # Test refund_atomic_swap + try: + result = await integration.refund_atomic_swap( + swap_id=swap_id, + contract_address=config.cross_chain_atomic_swap + ) + print(f"refund_atomic_swap result: {result}") + except Exception as e: + print(f"refund_atomic_swap error: {e}") + +if __name__ == "__main__": + asyncio.run(test_cli_client())