traia-iatp 0.1.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of traia-iatp might be problematic. Click here for more details.

Files changed (107) hide show
  1. traia_iatp/README.md +368 -0
  2. traia_iatp/__init__.py +54 -0
  3. traia_iatp/cli/__init__.py +5 -0
  4. traia_iatp/cli/main.py +483 -0
  5. traia_iatp/client/__init__.py +10 -0
  6. traia_iatp/client/a2a_client.py +274 -0
  7. traia_iatp/client/crewai_a2a_tools.py +335 -0
  8. traia_iatp/client/d402_a2a_client.py +293 -0
  9. traia_iatp/client/grpc_a2a_tools.py +349 -0
  10. traia_iatp/client/root_path_a2a_client.py +1 -0
  11. traia_iatp/contracts/__init__.py +12 -0
  12. traia_iatp/contracts/iatp_contracts_config.py +263 -0
  13. traia_iatp/contracts/wallet_creator.py +255 -0
  14. traia_iatp/core/__init__.py +43 -0
  15. traia_iatp/core/models.py +172 -0
  16. traia_iatp/d402/__init__.py +55 -0
  17. traia_iatp/d402/chains.py +102 -0
  18. traia_iatp/d402/client.py +150 -0
  19. traia_iatp/d402/clients/__init__.py +7 -0
  20. traia_iatp/d402/clients/base.py +218 -0
  21. traia_iatp/d402/clients/httpx.py +219 -0
  22. traia_iatp/d402/common.py +114 -0
  23. traia_iatp/d402/encoding.py +28 -0
  24. traia_iatp/d402/examples/client_example.py +197 -0
  25. traia_iatp/d402/examples/server_example.py +171 -0
  26. traia_iatp/d402/facilitator.py +453 -0
  27. traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
  28. traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
  29. traia_iatp/d402/fastmcp_middleware.py +147 -0
  30. traia_iatp/d402/mcp_middleware.py +434 -0
  31. traia_iatp/d402/middleware.py +193 -0
  32. traia_iatp/d402/models.py +116 -0
  33. traia_iatp/d402/networks.py +98 -0
  34. traia_iatp/d402/path.py +43 -0
  35. traia_iatp/d402/payment_introspection.py +104 -0
  36. traia_iatp/d402/payment_signing.py +178 -0
  37. traia_iatp/d402/paywall.py +119 -0
  38. traia_iatp/d402/starlette_middleware.py +326 -0
  39. traia_iatp/d402/template.py +1 -0
  40. traia_iatp/d402/types.py +300 -0
  41. traia_iatp/mcp/__init__.py +18 -0
  42. traia_iatp/mcp/client.py +201 -0
  43. traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
  44. traia_iatp/mcp/mcp_agent_template.py +481 -0
  45. traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
  46. traia_iatp/mcp/templates/README.md.j2 +310 -0
  47. traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
  48. traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
  49. traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
  50. traia_iatp/mcp/templates/dockerignore.j2 +47 -0
  51. traia_iatp/mcp/templates/env.example.j2 +57 -0
  52. traia_iatp/mcp/templates/gitignore.j2 +77 -0
  53. traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
  54. traia_iatp/mcp/templates/pyproject.toml.j2 +32 -0
  55. traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
  56. traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
  57. traia_iatp/mcp/templates/server.py.j2 +175 -0
  58. traia_iatp/mcp/traia_mcp_adapter.py +543 -0
  59. traia_iatp/preview_diagrams.html +181 -0
  60. traia_iatp/registry/__init__.py +26 -0
  61. traia_iatp/registry/atlas_search_indexes.json +280 -0
  62. traia_iatp/registry/embeddings.py +298 -0
  63. traia_iatp/registry/iatp_search_api.py +846 -0
  64. traia_iatp/registry/mongodb_registry.py +771 -0
  65. traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
  66. traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
  67. traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
  68. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
  69. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
  70. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
  71. traia_iatp/registry/readmes/README.md +251 -0
  72. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
  73. traia_iatp/scripts/__init__.py +2 -0
  74. traia_iatp/scripts/create_wallet.py +244 -0
  75. traia_iatp/server/__init__.py +15 -0
  76. traia_iatp/server/a2a_server.py +219 -0
  77. traia_iatp/server/example_template_usage.py +72 -0
  78. traia_iatp/server/iatp_server_agent_generator.py +237 -0
  79. traia_iatp/server/iatp_server_template_generator.py +235 -0
  80. traia_iatp/server/templates/.dockerignore.j2 +48 -0
  81. traia_iatp/server/templates/Dockerfile.j2 +49 -0
  82. traia_iatp/server/templates/README.md +137 -0
  83. traia_iatp/server/templates/README.md.j2 +425 -0
  84. traia_iatp/server/templates/__init__.py +1 -0
  85. traia_iatp/server/templates/__main__.py.j2 +565 -0
  86. traia_iatp/server/templates/agent.py.j2 +94 -0
  87. traia_iatp/server/templates/agent_config.json.j2 +22 -0
  88. traia_iatp/server/templates/agent_executor.py.j2 +279 -0
  89. traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
  90. traia_iatp/server/templates/env.example.j2 +84 -0
  91. traia_iatp/server/templates/gitignore.j2 +78 -0
  92. traia_iatp/server/templates/grpc_server.py.j2 +218 -0
  93. traia_iatp/server/templates/pyproject.toml.j2 +78 -0
  94. traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
  95. traia_iatp/server/templates/server.py.j2 +243 -0
  96. traia_iatp/special_agencies/__init__.py +4 -0
  97. traia_iatp/special_agencies/registry_search_agency.py +392 -0
  98. traia_iatp/utils/__init__.py +10 -0
  99. traia_iatp/utils/docker_utils.py +251 -0
  100. traia_iatp/utils/general.py +64 -0
  101. traia_iatp/utils/iatp_utils.py +126 -0
  102. traia_iatp-0.1.29.dist-info/METADATA +423 -0
  103. traia_iatp-0.1.29.dist-info/RECORD +107 -0
  104. traia_iatp-0.1.29.dist-info/WHEEL +5 -0
  105. traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
  106. traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
  107. traia_iatp-0.1.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,263 @@
1
+ """Contract configuration and utilities for IATP.
2
+
3
+ This module provides centralized contract configuration management for all IATP packages.
4
+ It loads contract addresses and ABIs from the iatp-contracts deployment artifacts.
5
+ """
6
+
7
+ import json
8
+ import os
9
+ from enum import Enum
10
+ from pathlib import Path
11
+ from typing import Dict, List, Optional
12
+
13
+ from web3 import Web3
14
+
15
+ # Contract types
16
+ class ContractName(str, Enum):
17
+ """Supported contract names."""
18
+ IATP_WALLET = "IATPWallet"
19
+ IATP_WALLET_FACTORY = "IATPWalletFactory"
20
+ IATP_SETTLEMENT_LAYER = "IATPSettlementLayer"
21
+ ROLE_MANAGER = "RoleManager"
22
+
23
+
24
+ # Supported networks
25
+ SUPPORTED_NETWORKS = ["sepolia", "base-sepolia", "arbitrum-sepolia", "localhost"]
26
+
27
+ # Default RPC URLs
28
+ DEFAULT_RPC_URLS = {
29
+ "sepolia": "https://ethereum-sepolia-rpc.publicnode.com",
30
+ "base-sepolia": "https://sepolia.base.org",
31
+ "arbitrum-sepolia": "https://sepolia-rollup.arbitrum.io/rpc",
32
+ "localhost": "http://127.0.0.1:8545"
33
+ }
34
+
35
+ # Cache for loaded data
36
+ _contract_addresses_cache: Dict[str, Dict] = {}
37
+ _contract_abis_cache: Dict[str, Dict] = {}
38
+
39
+
40
+ def _get_contracts_dir() -> Path:
41
+ """Get the contracts directory path.
42
+
43
+ Uses the package's data directory where ABIs and addresses are copied.
44
+ This directory should be updated manually whenever contracts are redeployed.
45
+
46
+ Location: IATP/src/traia_iatp/contracts/data/
47
+ """
48
+ # Use the data directory in the package
49
+ data_dir = Path(__file__).parent / "data"
50
+
51
+ # Verify it exists and has required subdirectories
52
+ abis_dir = data_dir / "abis"
53
+ addresses_dir = data_dir / "addresses"
54
+
55
+ if not data_dir.exists():
56
+ raise FileNotFoundError(
57
+ f"Contracts data directory not found: {data_dir}\n"
58
+ "Please ensure contract ABIs and addresses are copied to this location."
59
+ )
60
+
61
+ if not abis_dir.exists() or not addresses_dir.exists():
62
+ raise FileNotFoundError(
63
+ f"Contract ABIs or addresses directory missing in {data_dir}\n"
64
+ "Expected structure:\n"
65
+ " data/abis/contract-abis-*.json\n"
66
+ " data/addresses/contract-*.json"
67
+ )
68
+
69
+ # Verify files exist
70
+ if not list(abis_dir.glob("*.json")):
71
+ raise FileNotFoundError(f"No ABI files found in {abis_dir}")
72
+
73
+ if not list(addresses_dir.glob("*.json")):
74
+ raise FileNotFoundError(f"No address files found in {addresses_dir}")
75
+
76
+ return data_dir
77
+
78
+
79
+ def get_contract_address(contract_name: str, network: str = "sepolia") -> Optional[str]:
80
+ """Get contract address for a given network.
81
+
82
+ Args:
83
+ contract_name: Name of the contract (e.g., "IATPWallet")
84
+ network: Network name (default: "sepolia")
85
+
86
+ Returns:
87
+ Contract address as hex string, or None if not found
88
+ """
89
+ if network not in SUPPORTED_NETWORKS:
90
+ raise ValueError(f"Unsupported network: {network}. Supported: {SUPPORTED_NETWORKS}")
91
+
92
+ # Check cache
93
+ cache_key = f"{network}:addresses"
94
+ if cache_key not in _contract_addresses_cache:
95
+ # Load addresses file
96
+ contracts_dir = _get_contracts_dir()
97
+
98
+ # Get addresses directory (no symlinks - direct files)
99
+ addresses_dir = contracts_dir / "addresses"
100
+
101
+ if network == "localhost":
102
+ addresses_file = addresses_dir / "contract-addresses.json"
103
+ else:
104
+ addresses_file = addresses_dir / f"contract-proxies-{network}.json"
105
+
106
+ if not addresses_file.exists():
107
+ # Try without network suffix
108
+ addresses_file = addresses_dir / "contract-proxies.json"
109
+
110
+ if not addresses_file.exists():
111
+ return None
112
+
113
+ with open(addresses_file, 'r') as f:
114
+ data = json.load(f)
115
+
116
+ # Handle nested structure: {network: {contract: address}}
117
+ if network in data:
118
+ _contract_addresses_cache[cache_key] = data[network]
119
+ else:
120
+ # Flat structure: {contract: address}
121
+ _contract_addresses_cache[cache_key] = data
122
+
123
+ addresses = _contract_addresses_cache[cache_key]
124
+ return addresses.get(contract_name)
125
+
126
+
127
+ def get_contract_abi(contract_name: str, network: str = "sepolia") -> Optional[List[dict]]:
128
+ """Get contract ABI for a given network.
129
+
130
+ Args:
131
+ contract_name: Name of the contract (e.g., "IATPWallet")
132
+ network: Network name (default: "sepolia")
133
+
134
+ Returns:
135
+ Contract ABI as list of dicts, or None if not found
136
+ """
137
+ if network not in SUPPORTED_NETWORKS:
138
+ raise ValueError(f"Unsupported network: {network}. Supported: {SUPPORTED_NETWORKS}")
139
+
140
+ # Check cache
141
+ cache_key = f"{network}:abis"
142
+ if cache_key not in _contract_abis_cache:
143
+ # Load ABIs file
144
+ contracts_dir = _get_contracts_dir()
145
+
146
+ # Get ABIs directory (no symlinks - direct files)
147
+ abis_dir = contracts_dir / "abis"
148
+
149
+ abis_file = abis_dir / f"contract-abis-{network}.json"
150
+
151
+ if not abis_file.exists():
152
+ return None
153
+
154
+ with open(abis_file, 'r') as f:
155
+ data = json.load(f)
156
+
157
+ # Handle nested structure: {network: {contract: [abi]}}
158
+ if network in data:
159
+ # Data is nested by network
160
+ _contract_abis_cache[cache_key] = data[network]
161
+ else:
162
+ # Flat structure: {contract: {abi: [...]}} or {contract: [abi]}
163
+ _contract_abis_cache[cache_key] = data
164
+
165
+ abis = _contract_abis_cache[cache_key]
166
+
167
+ # Handle different ABI structures
168
+ contract_data = abis.get(contract_name)
169
+ if isinstance(contract_data, list):
170
+ # Direct ABI list: {contract: [abi]}
171
+ return contract_data
172
+ elif isinstance(contract_data, dict) and "abi" in contract_data:
173
+ # Wrapped ABI: {contract: {abi: [...]}}
174
+ return contract_data.get("abi")
175
+ else:
176
+ return None
177
+
178
+
179
+ def get_rpc_url(network: str = "sepolia") -> str:
180
+ """Get RPC URL for a network.
181
+
182
+ First checks environment variables, then falls back to defaults.
183
+
184
+ Environment variables:
185
+ - SEPOLIA_RPC_URL
186
+ - BASE_SEPOLIA_RPC_URL
187
+ - ARBITRUM_SEPOLIA_RPC_URL
188
+ - LOCALHOST_RPC_URL
189
+
190
+ Args:
191
+ network: Network name (default: "sepolia")
192
+
193
+ Returns:
194
+ RPC URL as string
195
+ """
196
+ if network not in SUPPORTED_NETWORKS:
197
+ raise ValueError(f"Unsupported network: {network}. Supported: {SUPPORTED_NETWORKS}")
198
+
199
+ # Check environment variable
200
+ env_var = f"{network.upper().replace('-', '_')}_RPC_URL"
201
+ rpc_url = os.getenv(env_var)
202
+
203
+ if rpc_url:
204
+ return rpc_url
205
+
206
+ # Fall back to default
207
+ return DEFAULT_RPC_URLS.get(network, DEFAULT_RPC_URLS["sepolia"])
208
+
209
+
210
+ def get_web3_provider(network: str = "sepolia") -> Web3:
211
+ """Get Web3 provider for a network.
212
+
213
+ Args:
214
+ network: Network name (default: "sepolia")
215
+
216
+ Returns:
217
+ Web3 instance connected to the network
218
+ """
219
+ rpc_url = get_rpc_url(network)
220
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
221
+
222
+ if not w3.is_connected():
223
+ raise ConnectionError(f"Failed to connect to {network} at {rpc_url}")
224
+
225
+ return w3
226
+
227
+
228
+ def load_contract(
229
+ contract_name: str,
230
+ network: str = "sepolia",
231
+ address: Optional[str] = None
232
+ ):
233
+ """Load a contract instance with Web3.
234
+
235
+ Args:
236
+ contract_name: Name of the contract
237
+ network: Network name (default: "sepolia")
238
+ address: Optional contract address (uses deployed address if not provided)
239
+
240
+ Returns:
241
+ Web3 contract instance
242
+ """
243
+ w3 = get_web3_provider(network)
244
+
245
+ # Get ABI
246
+ abi = get_contract_abi(contract_name, network)
247
+ if not abi:
248
+ raise ValueError(f"ABI not found for {contract_name} on {network}")
249
+
250
+ # Get address
251
+ if not address:
252
+ address = get_contract_address(contract_name, network)
253
+ if not address:
254
+ raise ValueError(f"Address not found for {contract_name} on {network}")
255
+
256
+ # Create contract instance
257
+ contract = w3.eth.contract(
258
+ address=Web3.to_checksum_address(address),
259
+ abi=abi
260
+ )
261
+
262
+ return contract, w3
263
+
@@ -0,0 +1,255 @@
1
+ """IATPWallet creation script using IATPWalletFactory.
2
+
3
+ This script creates a new IATPWallet by calling the IATPWalletFactory contract.
4
+ It generates an operator keypair and deploys a wallet for a given owner.
5
+
6
+ Uses centralized contract configuration from traia_iatp.contracts.config.
7
+ """
8
+
9
+ import os
10
+ import json
11
+ from typing import Dict, Optional, Tuple
12
+ from pathlib import Path
13
+ from eth_account import Account
14
+ from web3 import Web3
15
+ from web3.contract import Contract
16
+
17
+
18
+ def get_contract_config(network: str = "sepolia") -> Dict:
19
+ """Load contract addresses and ABIs for a network.
20
+
21
+ Uses the centralized config module from traia_iatp.contracts.config.
22
+
23
+ Args:
24
+ network: Network name (sepolia, base-sepolia, etc.)
25
+
26
+ Returns:
27
+ Dict with addresses and ABIs
28
+ """
29
+ from traia_iatp.contracts.iatp_contracts_config import get_contract_address, get_contract_abi
30
+
31
+ # Get all contract addresses and ABIs
32
+ contract_names = ["IATPWalletFactory", "IATPSettlementLayer", "IATPWallet", "RoleManager"]
33
+ addresses = {}
34
+ abis = {}
35
+
36
+ for name in contract_names:
37
+ try:
38
+ address = get_contract_address(name, network)
39
+ if address:
40
+ addresses[name] = address
41
+
42
+ abi = get_contract_abi(name, network)
43
+ if abi:
44
+ abis[name] = {"abi": abi}
45
+ except Exception as e:
46
+ # Some contracts may not be on all networks
47
+ pass
48
+
49
+ return {
50
+ "addresses": addresses,
51
+ "abis": abis
52
+ }
53
+
54
+
55
+ def create_wallet(
56
+ owner_private_key: str,
57
+ network: str = "sepolia",
58
+ rpc_url: Optional[str] = None,
59
+ maintainer_private_key: Optional[str] = None
60
+ ) -> Tuple[str, str, str]:
61
+ """Create a new IATPWallet using IATPWalletFactory.
62
+
63
+ Args:
64
+ owner_private_key: Private key of the wallet owner
65
+ network: Network name (default: sepolia)
66
+ rpc_url: Optional RPC URL (uses default if not provided)
67
+ maintainer_private_key: Optional maintainer key (if calling createWalletFor)
68
+
69
+ Returns:
70
+ Tuple of (wallet_address, operator_address, operator_private_key)
71
+ """
72
+ # Setup Web3
73
+ if not rpc_url:
74
+ rpc_urls = {
75
+ "sepolia": os.getenv("SEPOLIA_RPC_URL", "https://ethereum-sepolia-rpc.publicnode.com"),
76
+ "base-sepolia": os.getenv("BASE_SEPOLIA_RPC_URL", "https://sepolia.base.org"),
77
+ "arbitrum-sepolia": os.getenv("ARBITRUM_SEPOLIA_RPC_URL", "https://sepolia-rollup.arbitrum.io/rpc"),
78
+ }
79
+ rpc_url = rpc_urls.get(network)
80
+
81
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
82
+
83
+ if not w3.is_connected():
84
+ raise ConnectionError(f"Could not connect to {network} at {rpc_url}")
85
+
86
+ print(f"āœ… Connected to {network}")
87
+ print(f" RPC: {rpc_url}")
88
+ print(f" Chain ID: {w3.eth.chain_id}")
89
+
90
+ # Load contract config
91
+ config = get_contract_config(network)
92
+ factory_address = config["addresses"].get("IATPWalletFactory")
93
+ factory_abi = config["abis"].get("IATPWalletFactory", {}).get("abi")
94
+
95
+ if not factory_address:
96
+ raise ValueError(f"IATPWalletFactory address not found for {network}")
97
+
98
+ if not factory_abi:
99
+ raise ValueError(f"IATPWalletFactory ABI not found for {network}")
100
+
101
+ print(f"\nšŸ“ Contract Configuration:")
102
+ print(f" Factory: {factory_address}")
103
+
104
+ # Create owner account
105
+ if owner_private_key.startswith("0x"):
106
+ owner_private_key = owner_private_key[2:]
107
+ owner_account = Account.from_key(owner_private_key)
108
+ print(f"\nšŸ‘¤ Owner Account: {owner_account.address}")
109
+
110
+ # Check owner balance
111
+ owner_balance = w3.eth.get_balance(owner_account.address)
112
+ print(f" Balance: {w3.from_wei(owner_balance, 'ether')} ETH")
113
+
114
+ if owner_balance == 0:
115
+ print(f" āš ļø Warning: Owner has no ETH for gas")
116
+
117
+ # Generate operator keypair
118
+ operator_account = Account.create()
119
+ operator_address = operator_account.address
120
+ operator_private_key = operator_account.key.hex()
121
+
122
+ print(f"\nšŸ”‘ Generated Operator:")
123
+ print(f" Address: {operator_address}")
124
+ print(f" Private Key: {operator_private_key}")
125
+
126
+ # Create factory contract instance
127
+ factory_contract = w3.eth.contract(
128
+ address=Web3.to_checksum_address(factory_address),
129
+ abi=factory_abi
130
+ )
131
+
132
+ # Determine which method to call
133
+ if maintainer_private_key:
134
+ # Use createWalletFor (maintainer only)
135
+ if maintainer_private_key.startswith("0x"):
136
+ maintainer_private_key = maintainer_private_key[2:]
137
+ caller_account = Account.from_key(maintainer_private_key)
138
+
139
+ print(f"\nšŸ“ž Calling createWalletFor() as maintainer")
140
+ print(f" Maintainer: {caller_account.address}")
141
+
142
+ # Build transaction
143
+ tx = factory_contract.functions.createWalletFor(
144
+ owner_account.address,
145
+ operator_address
146
+ ).build_transaction({
147
+ 'from': caller_account.address,
148
+ 'nonce': w3.eth.get_transaction_count(caller_account.address),
149
+ 'gas': 2000000,
150
+ 'gasPrice': w3.eth.gas_price,
151
+ 'chainId': w3.eth.chain_id
152
+ })
153
+
154
+ # Sign and send
155
+ signed_tx = w3.eth.account.sign_transaction(tx, maintainer_private_key)
156
+ else:
157
+ # Use createWallet (owner calls)
158
+ print(f"\nšŸ“ž Calling createWallet() as owner")
159
+
160
+ # Build transaction
161
+ tx = factory_contract.functions.createWallet(
162
+ operator_address
163
+ ).build_transaction({
164
+ 'from': owner_account.address,
165
+ 'nonce': w3.eth.get_transaction_count(owner_account.address),
166
+ 'gas': 2000000,
167
+ 'gasPrice': w3.eth.gas_price,
168
+ 'chainId': w3.eth.chain_id
169
+ })
170
+
171
+ # Sign and send
172
+ signed_tx = w3.eth.account.sign_transaction(tx, owner_private_key)
173
+
174
+ print(f"\nšŸš€ Sending transaction...")
175
+ tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
176
+ print(f" TX Hash: {tx_hash.hex()}")
177
+
178
+ print(f"\nā³ Waiting for confirmation...")
179
+ tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
180
+
181
+ if tx_receipt['status'] == 1:
182
+ print(f"āœ… Transaction confirmed!")
183
+ print(f" Block: {tx_receipt['blockNumber']}")
184
+ print(f" Gas Used: {tx_receipt['gasUsed']}")
185
+
186
+ # Parse logs to get wallet address
187
+ wallet_created_event = factory_contract.events.WalletCreated()
188
+ logs = wallet_created_event.process_receipt(tx_receipt)
189
+
190
+ if logs:
191
+ wallet_address = logs[0]['args']['wallet']
192
+ print(f"\nšŸŽ‰ Wallet Created!")
193
+ print(f" Wallet Address: {wallet_address}")
194
+ print(f" Owner: {logs[0]['args']['owner']}")
195
+ print(f" Operator: {logs[0]['args']['operatorAddress']}")
196
+
197
+ return wallet_address, operator_address, operator_private_key
198
+ else:
199
+ raise Exception("WalletCreated event not found in logs")
200
+ else:
201
+ raise Exception(f"Transaction failed: {tx_receipt}")
202
+
203
+
204
+ def main():
205
+ """CLI entry point for wallet creation."""
206
+ import argparse
207
+
208
+ parser = argparse.ArgumentParser(description="Create IATPWallet using IATPWalletFactory")
209
+ parser.add_argument("--owner-key", required=True, help="Owner's private key")
210
+ parser.add_argument("--network", default="sepolia", help="Network name (default: sepolia)")
211
+ parser.add_argument("--rpc-url", help="Custom RPC URL")
212
+ parser.add_argument("--maintainer-key", help="Maintainer key (for createWalletFor)")
213
+ parser.add_argument("--output", help="Output file for wallet info (JSON)")
214
+
215
+ args = parser.parse_args()
216
+
217
+ try:
218
+ wallet_address, operator_address, operator_private_key = create_wallet(
219
+ owner_private_key=args.owner_key,
220
+ network=args.network,
221
+ rpc_url=args.rpc_url,
222
+ maintainer_private_key=args.maintainer_key
223
+ )
224
+
225
+ result = {
226
+ "wallet_address": wallet_address,
227
+ "operator_address": operator_address,
228
+ "operator_private_key": operator_private_key,
229
+ "owner_address": Account.from_key(args.owner_key).address,
230
+ "network": args.network
231
+ }
232
+
233
+ if args.output:
234
+ with open(args.output, 'w') as f:
235
+ json.dump(result, f, indent=2)
236
+ print(f"\nšŸ’¾ Wallet info saved to: {args.output}")
237
+
238
+ print(f"\n{'='*80}")
239
+ print("Wallet Created Successfully!")
240
+ print(f"{'='*80}")
241
+ print(json.dumps(result, indent=2))
242
+
243
+ except Exception as e:
244
+ print(f"\nāŒ Error: {e}")
245
+ import traceback
246
+ traceback.print_exc()
247
+ return 1
248
+
249
+ return 0
250
+
251
+
252
+ if __name__ == "__main__":
253
+ import sys
254
+ sys.exit(main())
255
+
@@ -0,0 +1,43 @@
1
+ """Core data models for IATP."""
2
+
3
+ from .models import (
4
+ # Core server models
5
+ MCPServer,
6
+ MCPServerType,
7
+
8
+ # Agent models
9
+ AgentSkill,
10
+ AgentCapabilities,
11
+ AgentCard,
12
+
13
+ # IATP protocol models
14
+ IATPEndpoints,
15
+ IATPRequest,
16
+ IATPResponse,
17
+
18
+ # Utility agent models
19
+ UtilityAgent,
20
+ UtilityAgentStatus,
21
+ UtilityAgentRegistryEntry,
22
+ )
23
+
24
+ __all__ = [
25
+ # Core server models
26
+ "MCPServer",
27
+ "MCPServerType",
28
+
29
+ # Agent models
30
+ "AgentSkill",
31
+ "AgentCapabilities",
32
+ "AgentCard",
33
+
34
+ # IATP protocol models
35
+ "IATPEndpoints",
36
+ "IATPRequest",
37
+ "IATPResponse",
38
+
39
+ # Utility agent models
40
+ "UtilityAgent",
41
+ "UtilityAgentStatus",
42
+ "UtilityAgentRegistryEntry",
43
+ ]