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.
- traia_iatp/README.md +368 -0
- traia_iatp/__init__.py +54 -0
- traia_iatp/cli/__init__.py +5 -0
- traia_iatp/cli/main.py +483 -0
- traia_iatp/client/__init__.py +10 -0
- traia_iatp/client/a2a_client.py +274 -0
- traia_iatp/client/crewai_a2a_tools.py +335 -0
- traia_iatp/client/d402_a2a_client.py +293 -0
- traia_iatp/client/grpc_a2a_tools.py +349 -0
- traia_iatp/client/root_path_a2a_client.py +1 -0
- traia_iatp/contracts/__init__.py +12 -0
- traia_iatp/contracts/iatp_contracts_config.py +263 -0
- traia_iatp/contracts/wallet_creator.py +255 -0
- traia_iatp/core/__init__.py +43 -0
- traia_iatp/core/models.py +172 -0
- traia_iatp/d402/__init__.py +55 -0
- traia_iatp/d402/chains.py +102 -0
- traia_iatp/d402/client.py +150 -0
- traia_iatp/d402/clients/__init__.py +7 -0
- traia_iatp/d402/clients/base.py +218 -0
- traia_iatp/d402/clients/httpx.py +219 -0
- traia_iatp/d402/common.py +114 -0
- traia_iatp/d402/encoding.py +28 -0
- traia_iatp/d402/examples/client_example.py +197 -0
- traia_iatp/d402/examples/server_example.py +171 -0
- traia_iatp/d402/facilitator.py +453 -0
- traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
- traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
- traia_iatp/d402/fastmcp_middleware.py +147 -0
- traia_iatp/d402/mcp_middleware.py +434 -0
- traia_iatp/d402/middleware.py +193 -0
- traia_iatp/d402/models.py +116 -0
- traia_iatp/d402/networks.py +98 -0
- traia_iatp/d402/path.py +43 -0
- traia_iatp/d402/payment_introspection.py +104 -0
- traia_iatp/d402/payment_signing.py +178 -0
- traia_iatp/d402/paywall.py +119 -0
- traia_iatp/d402/starlette_middleware.py +326 -0
- traia_iatp/d402/template.py +1 -0
- traia_iatp/d402/types.py +300 -0
- traia_iatp/mcp/__init__.py +18 -0
- traia_iatp/mcp/client.py +201 -0
- traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
- traia_iatp/mcp/mcp_agent_template.py +481 -0
- traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
- traia_iatp/mcp/templates/README.md.j2 +310 -0
- traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
- traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
- traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
- traia_iatp/mcp/templates/dockerignore.j2 +47 -0
- traia_iatp/mcp/templates/env.example.j2 +57 -0
- traia_iatp/mcp/templates/gitignore.j2 +77 -0
- traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
- traia_iatp/mcp/templates/pyproject.toml.j2 +32 -0
- traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
- traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
- traia_iatp/mcp/templates/server.py.j2 +175 -0
- traia_iatp/mcp/traia_mcp_adapter.py +543 -0
- traia_iatp/preview_diagrams.html +181 -0
- traia_iatp/registry/__init__.py +26 -0
- traia_iatp/registry/atlas_search_indexes.json +280 -0
- traia_iatp/registry/embeddings.py +298 -0
- traia_iatp/registry/iatp_search_api.py +846 -0
- traia_iatp/registry/mongodb_registry.py +771 -0
- traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
- traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
- traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
- traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
- traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
- traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
- traia_iatp/registry/readmes/README.md +251 -0
- traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
- traia_iatp/scripts/__init__.py +2 -0
- traia_iatp/scripts/create_wallet.py +244 -0
- traia_iatp/server/__init__.py +15 -0
- traia_iatp/server/a2a_server.py +219 -0
- traia_iatp/server/example_template_usage.py +72 -0
- traia_iatp/server/iatp_server_agent_generator.py +237 -0
- traia_iatp/server/iatp_server_template_generator.py +235 -0
- traia_iatp/server/templates/.dockerignore.j2 +48 -0
- traia_iatp/server/templates/Dockerfile.j2 +49 -0
- traia_iatp/server/templates/README.md +137 -0
- traia_iatp/server/templates/README.md.j2 +425 -0
- traia_iatp/server/templates/__init__.py +1 -0
- traia_iatp/server/templates/__main__.py.j2 +565 -0
- traia_iatp/server/templates/agent.py.j2 +94 -0
- traia_iatp/server/templates/agent_config.json.j2 +22 -0
- traia_iatp/server/templates/agent_executor.py.j2 +279 -0
- traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
- traia_iatp/server/templates/env.example.j2 +84 -0
- traia_iatp/server/templates/gitignore.j2 +78 -0
- traia_iatp/server/templates/grpc_server.py.j2 +218 -0
- traia_iatp/server/templates/pyproject.toml.j2 +78 -0
- traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
- traia_iatp/server/templates/server.py.j2 +243 -0
- traia_iatp/special_agencies/__init__.py +4 -0
- traia_iatp/special_agencies/registry_search_agency.py +392 -0
- traia_iatp/utils/__init__.py +10 -0
- traia_iatp/utils/docker_utils.py +251 -0
- traia_iatp/utils/general.py +64 -0
- traia_iatp/utils/iatp_utils.py +126 -0
- traia_iatp-0.1.29.dist-info/METADATA +423 -0
- traia_iatp-0.1.29.dist-info/RECORD +107 -0
- traia_iatp-0.1.29.dist-info/WHEEL +5 -0
- traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
- traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
- 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
|
+
]
|