traia-iatp 0.1.2__py3-none-any.whl → 0.1.67__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.
- traia_iatp/__init__.py +105 -8
- traia_iatp/cli/main.py +85 -1
- traia_iatp/client/__init__.py +28 -3
- traia_iatp/client/crewai_a2a_tools.py +32 -12
- traia_iatp/client/d402_a2a_client.py +348 -0
- traia_iatp/contracts/__init__.py +11 -0
- traia_iatp/contracts/data/abis/contract-abis-localhost.json +4091 -0
- traia_iatp/contracts/data/abis/contract-abis-sepolia.json +4890 -0
- traia_iatp/contracts/data/addresses/contract-addresses.json +17 -0
- traia_iatp/contracts/data/addresses/contract-proxies.json +12 -0
- traia_iatp/contracts/iatp_contracts_config.py +263 -0
- traia_iatp/contracts/wallet_creator.py +369 -0
- traia_iatp/core/models.py +17 -3
- traia_iatp/d402/MIDDLEWARE_ARCHITECTURE.md +205 -0
- traia_iatp/d402/PRICE_BUILDER_USAGE.md +249 -0
- traia_iatp/d402/README.md +489 -0
- traia_iatp/d402/__init__.py +54 -0
- traia_iatp/d402/asgi_wrapper.py +469 -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 +266 -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 +481 -0
- traia_iatp/d402/mcp_middleware.py +296 -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 +126 -0
- traia_iatp/d402/payment_signing.py +183 -0
- traia_iatp/d402/price_builder.py +164 -0
- traia_iatp/d402/servers/__init__.py +61 -0
- traia_iatp/d402/servers/base.py +139 -0
- traia_iatp/d402/servers/example_general_server.py +140 -0
- traia_iatp/d402/servers/fastapi.py +253 -0
- traia_iatp/d402/servers/mcp.py +304 -0
- traia_iatp/d402/servers/starlette.py +878 -0
- traia_iatp/d402/starlette_middleware.py +529 -0
- traia_iatp/d402/types.py +300 -0
- traia_iatp/mcp/D402_MCP_ADAPTER_FLOW.md +357 -0
- traia_iatp/mcp/__init__.py +3 -0
- traia_iatp/mcp/d402_mcp_tool_adapter.py +526 -0
- traia_iatp/mcp/mcp_agent_template.py +78 -13
- traia_iatp/mcp/templates/Dockerfile.j2 +27 -4
- traia_iatp/mcp/templates/README.md.j2 +104 -8
- traia_iatp/mcp/templates/cursor-rules.md.j2 +194 -0
- traia_iatp/mcp/templates/deployment_params.json.j2 +1 -2
- traia_iatp/mcp/templates/docker-compose.yml.j2 +13 -3
- traia_iatp/mcp/templates/env.example.j2 +60 -0
- traia_iatp/mcp/templates/mcp_health_check.py.j2 +2 -2
- traia_iatp/mcp/templates/pyproject.toml.j2 +11 -5
- traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
- traia_iatp/mcp/templates/run_local_docker.sh.j2 +320 -10
- traia_iatp/mcp/templates/server.py.j2 +174 -197
- traia_iatp/mcp/traia_mcp_adapter.py +182 -20
- traia_iatp/registry/__init__.py +47 -12
- traia_iatp/registry/atlas_search_indexes.json +108 -54
- traia_iatp/registry/iatp_search_api.py +169 -39
- traia_iatp/registry/mongodb_registry.py +241 -69
- traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +1 -1
- traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +8 -8
- traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +1 -1
- traia_iatp/registry/readmes/README.md +3 -3
- traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +6 -6
- traia_iatp/scripts/__init__.py +2 -0
- traia_iatp/scripts/create_wallet.py +244 -0
- traia_iatp/server/a2a_server.py +22 -7
- traia_iatp/server/iatp_server_template_generator.py +23 -0
- traia_iatp/server/templates/.dockerignore.j2 +48 -0
- traia_iatp/server/templates/Dockerfile.j2 +23 -1
- traia_iatp/server/templates/README.md +2 -2
- traia_iatp/server/templates/README.md.j2 +5 -5
- traia_iatp/server/templates/__main__.py.j2 +374 -66
- traia_iatp/server/templates/agent.py.j2 +12 -11
- traia_iatp/server/templates/agent_config.json.j2 +3 -3
- traia_iatp/server/templates/agent_executor.py.j2 +45 -27
- traia_iatp/server/templates/env.example.j2 +32 -4
- traia_iatp/server/templates/gitignore.j2 +7 -0
- traia_iatp/server/templates/pyproject.toml.j2 +13 -12
- traia_iatp/server/templates/run_local_docker.sh.j2 +143 -11
- traia_iatp/server/templates/server.py.j2 +197 -10
- traia_iatp/special_agencies/registry_search_agency.py +1 -1
- traia_iatp/utils/iatp_utils.py +6 -6
- traia_iatp-0.1.67.dist-info/METADATA +320 -0
- traia_iatp-0.1.67.dist-info/RECORD +117 -0
- traia_iatp-0.1.2.dist-info/METADATA +0 -414
- traia_iatp-0.1.2.dist-info/RECORD +0 -72
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/WHEEL +0 -0
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/entry_points.txt +0 -0
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/licenses/LICENSE +0 -0
- {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/top_level.txt +0 -0
traia_iatp/core/models.py
CHANGED
|
@@ -21,7 +21,7 @@ class MCPServer(BaseModel):
|
|
|
21
21
|
capabilities: List[str] = Field(default_factory=list, description="List of capabilities/APIs")
|
|
22
22
|
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
23
23
|
|
|
24
|
-
class
|
|
24
|
+
class ConfigDict:
|
|
25
25
|
json_encoders = {HttpUrl: str}
|
|
26
26
|
|
|
27
27
|
|
|
@@ -105,6 +105,12 @@ class UtilityAgent(BaseModel):
|
|
|
105
105
|
github_repo: Optional[str] = Field(None, description="GitHub repository URL")
|
|
106
106
|
cloud_run_url: Optional[str] = Field(None, description="Cloud Run deployment URL")
|
|
107
107
|
|
|
108
|
+
# X402 payment configuration
|
|
109
|
+
contract_address: Optional[str] = Field(None, description="On-chain utility agent contract address")
|
|
110
|
+
operator_address: Optional[str] = Field(None, description="Operator address for signing attestations")
|
|
111
|
+
d402_enabled: bool = Field(default=False, description="Whether d402 payments are enabled")
|
|
112
|
+
d402_config: Optional[Dict[str, Any]] = Field(None, description="X402 payment configuration")
|
|
113
|
+
|
|
108
114
|
# Search and discovery
|
|
109
115
|
search_text: Optional[str] = Field(None, description="Concatenated searchable text")
|
|
110
116
|
tags: List[str] = Field(default_factory=list, description="Tags for search and categorization")
|
|
@@ -113,7 +119,7 @@ class UtilityAgent(BaseModel):
|
|
|
113
119
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
114
120
|
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
115
121
|
|
|
116
|
-
class
|
|
122
|
+
class ConfigDict:
|
|
117
123
|
json_encoders = {HttpUrl: str, datetime: lambda v: v.isoformat()}
|
|
118
124
|
|
|
119
125
|
|
|
@@ -124,6 +130,9 @@ class UtilityAgentRegistryEntry(BaseModel):
|
|
|
124
130
|
name: str = Field(..., description="Name for discovery")
|
|
125
131
|
description: str = Field(..., description="Description for search")
|
|
126
132
|
|
|
133
|
+
# MCP server reference
|
|
134
|
+
mcp_server_id: Optional[str] = Field(None, description="ID of the MCP server this agent wraps")
|
|
135
|
+
|
|
127
136
|
# Base URL for the agent - all endpoints are derived from this
|
|
128
137
|
base_url: Optional[str] = Field(None, description="Base URL of the deployed agent")
|
|
129
138
|
|
|
@@ -135,6 +144,11 @@ class UtilityAgentRegistryEntry(BaseModel):
|
|
|
135
144
|
skills: List[AgentSkill] = Field(default_factory=list, description="Detailed skills from agent card")
|
|
136
145
|
tags: List[str] = Field(default_factory=list, description="Tags for search")
|
|
137
146
|
|
|
147
|
+
# X402 payment information
|
|
148
|
+
contract_address: Optional[str] = Field(None, description="On-chain utility agent contract address")
|
|
149
|
+
d402_enabled: bool = Field(default=False, description="Whether d402 payments are enabled")
|
|
150
|
+
d402_payment_info: Optional[Dict[str, Any]] = Field(None, description="X402 payment information")
|
|
151
|
+
|
|
138
152
|
# Search optimization
|
|
139
153
|
search_text: Optional[str] = Field(None, description="Full text for search")
|
|
140
154
|
|
|
@@ -143,7 +157,7 @@ class UtilityAgentRegistryEntry(BaseModel):
|
|
|
143
157
|
last_health_check: Optional[datetime] = Field(None, description="Last successful health check")
|
|
144
158
|
is_active: bool = Field(default=True, description="Whether the agent is active")
|
|
145
159
|
|
|
146
|
-
class
|
|
160
|
+
class ConfigDict:
|
|
147
161
|
json_encoders = {HttpUrl: str, datetime: lambda v: v.isoformat()}
|
|
148
162
|
|
|
149
163
|
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# D402 Payment Middleware Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The D402 payment protocol provides two middleware implementations to support different server types:
|
|
6
|
+
|
|
7
|
+
1. **`d402/starlette_middleware.py`** - MCP-specific (legacy, backwards compatible)
|
|
8
|
+
2. **`d402/servers/starlette.py`** - Generalized (recommended for new code)
|
|
9
|
+
|
|
10
|
+
## Middleware Comparison
|
|
11
|
+
|
|
12
|
+
| Feature | `starlette_middleware.py` | `servers/starlette.py` |
|
|
13
|
+
|---------|--------------------------|------------------------|
|
|
14
|
+
| **Use Case** | MCP servers only | Any Starlette server |
|
|
15
|
+
| **Endpoint Patterns** | `/mcp` only | `/mcp`, `/`, `*`, custom |
|
|
16
|
+
| **Configuration** | `tool_payment_configs` only | `tool_payment_configs` OR `price`+`endpoint_patterns` |
|
|
17
|
+
| **Type Safety** | Dict-based configs | Supports `TokenAmount`/`TokenAsset` objects |
|
|
18
|
+
| **Status** | Maintained for compatibility | Recommended for new code |
|
|
19
|
+
|
|
20
|
+
## Usage Patterns
|
|
21
|
+
|
|
22
|
+
### Pattern 1: MCP Servers (with decorators)
|
|
23
|
+
|
|
24
|
+
MCP servers use decorators like `@require_payment_for_tool()` which auto-generate payment configs.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from traia_iatp.d402.starlette_middleware import D402PaymentMiddleware
|
|
28
|
+
from traia_iatp.d402.payment_introspection import extract_payment_configs_from_mcp
|
|
29
|
+
|
|
30
|
+
# Extract configs from decorators
|
|
31
|
+
tool_payment_configs = extract_payment_configs_from_mcp(mcp, SERVER_ADDRESS)
|
|
32
|
+
|
|
33
|
+
# Add middleware
|
|
34
|
+
app.add_middleware(
|
|
35
|
+
D402PaymentMiddleware,
|
|
36
|
+
tool_payment_configs=tool_payment_configs,
|
|
37
|
+
server_address=SERVER_ADDRESS,
|
|
38
|
+
requires_auth=True,
|
|
39
|
+
internal_api_key=API_KEY
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Files using this pattern:**
|
|
44
|
+
- `mcp/templates/server.py.j2` (MCP server template)
|
|
45
|
+
- Generated MCP servers (e.g., `coingecko-api-mcp-server`)
|
|
46
|
+
|
|
47
|
+
### Pattern 2: A2A Servers (with price + patterns)
|
|
48
|
+
|
|
49
|
+
A2A servers don't use decorators - they specify price and endpoint patterns directly.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from traia_iatp.d402.servers import D402PaymentMiddleware
|
|
53
|
+
from traia_iatp.d402.types import TokenAmount, TokenAsset, EIP712Domain
|
|
54
|
+
|
|
55
|
+
# Create price with proper types
|
|
56
|
+
price = TokenAmount(
|
|
57
|
+
amount="10000", # 0.01 USDC
|
|
58
|
+
asset=TokenAsset(
|
|
59
|
+
address="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
60
|
+
decimals=6,
|
|
61
|
+
network="sepolia",
|
|
62
|
+
eip712=EIP712Domain(
|
|
63
|
+
name="IATPWallet",
|
|
64
|
+
version="1"
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Add middleware with price + patterns
|
|
70
|
+
app.add_middleware(
|
|
71
|
+
D402PaymentMiddleware,
|
|
72
|
+
price=price,
|
|
73
|
+
endpoint_patterns=["/"], # Protect root endpoint
|
|
74
|
+
server_address=CONTRACT_ADDRESS,
|
|
75
|
+
description="A2A request",
|
|
76
|
+
requires_auth=False # Payment-only (no API keys)
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Files using this pattern:**
|
|
81
|
+
- `server/templates/__main__.py.j2` (A2A utility agent template)
|
|
82
|
+
- Generated utility agents
|
|
83
|
+
|
|
84
|
+
### Pattern 3: General Servers (with tool configs)
|
|
85
|
+
|
|
86
|
+
General servers can also use the generalized middleware with pre-built configs.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from traia_iatp.d402.servers import D402PaymentMiddleware
|
|
90
|
+
|
|
91
|
+
# Build configs manually
|
|
92
|
+
tool_payment_configs = {
|
|
93
|
+
"/api/analyze": {
|
|
94
|
+
"price_wei": "10000",
|
|
95
|
+
"token_address": "0x...",
|
|
96
|
+
# ... full config
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Add middleware
|
|
101
|
+
app.add_middleware(
|
|
102
|
+
D402PaymentMiddleware,
|
|
103
|
+
tool_payment_configs=tool_payment_configs,
|
|
104
|
+
server_address=SERVER_ADDRESS
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Endpoint Pattern Matching
|
|
109
|
+
|
|
110
|
+
The generalized middleware supports multiple endpoint patterns:
|
|
111
|
+
|
|
112
|
+
| Pattern | Example | Matches |
|
|
113
|
+
|---------|---------|---------|
|
|
114
|
+
| **MCP** | `/mcp` with `tools/call` | MCP tool calls: `/mcp/tools/{tool_name}` |
|
|
115
|
+
| **A2A** | `/` | A2A JSON-RPC at root path |
|
|
116
|
+
| **Exact Match** | `/api/analyze` | Only `/api/analyze` |
|
|
117
|
+
| **Wildcard** | `*` | All POST endpoints |
|
|
118
|
+
|
|
119
|
+
## Migration Guide
|
|
120
|
+
|
|
121
|
+
### Existing MCP Servers
|
|
122
|
+
**No action needed** - continue using `starlette_middleware.py`
|
|
123
|
+
|
|
124
|
+
### New MCP Servers
|
|
125
|
+
Can use either middleware (both work the same for MCP)
|
|
126
|
+
|
|
127
|
+
### New A2A/Utility Servers
|
|
128
|
+
**Must use** `servers/starlette.py` with `price` + `endpoint_patterns`
|
|
129
|
+
|
|
130
|
+
### Custom Servers
|
|
131
|
+
**Recommended** to use `servers/starlette.py` for flexibility
|
|
132
|
+
|
|
133
|
+
## Code Organization
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
traia_iatp/
|
|
137
|
+
├── d402/
|
|
138
|
+
│ ├── starlette_middleware.py # MCP-specific (legacy)
|
|
139
|
+
│ ├── servers/
|
|
140
|
+
│ │ ├── __init__.py
|
|
141
|
+
│ │ └── starlette.py # Generalized (recommended)
|
|
142
|
+
│ ├── types.py # TokenAmount, TokenAsset, etc.
|
|
143
|
+
│ └── ...
|
|
144
|
+
├── mcp/
|
|
145
|
+
│ └── templates/
|
|
146
|
+
│ └── server.py.j2 # Uses starlette_middleware.py
|
|
147
|
+
└── server/
|
|
148
|
+
└── templates/
|
|
149
|
+
└── __main__.py.j2 # Uses servers/starlette.py
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Key Differences
|
|
153
|
+
|
|
154
|
+
### Configuration Building
|
|
155
|
+
|
|
156
|
+
**MCP (decorator-based):**
|
|
157
|
+
```python
|
|
158
|
+
# Configs extracted from @require_payment_for_tool decorators
|
|
159
|
+
tool_payment_configs = extract_payment_configs_from_mcp(mcp, SERVER_ADDRESS)
|
|
160
|
+
# Result: {"tool_name": {"price_wei": "...", "token_address": "...", ...}}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**A2A (price-based):**
|
|
164
|
+
```python
|
|
165
|
+
# Price defined with type-safe objects
|
|
166
|
+
price = TokenAmount(amount="10000", asset=TokenAsset(...))
|
|
167
|
+
# Middleware builds configs internally: {"/": {"price_wei": "10000", ...}}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Endpoint Protection
|
|
171
|
+
|
|
172
|
+
**MCP:**
|
|
173
|
+
- Protects individual tools within `/mcp` endpoint
|
|
174
|
+
- Each tool can have different prices
|
|
175
|
+
- Example: `/mcp/tools/get_price` costs 0.001 USDC
|
|
176
|
+
|
|
177
|
+
**A2A:**
|
|
178
|
+
- Protects entire endpoint (usually `/`)
|
|
179
|
+
- Single price for all methods on that endpoint
|
|
180
|
+
- Example: All JSON-RPC methods to `/` cost 0.01 USDC
|
|
181
|
+
|
|
182
|
+
## Testing
|
|
183
|
+
|
|
184
|
+
Both middlewares are tested through their respective server types:
|
|
185
|
+
|
|
186
|
+
**MCP Servers:**
|
|
187
|
+
```bash
|
|
188
|
+
# Test MCP server with D402
|
|
189
|
+
uv run pytest automated-tests/mcp/coingecko-api-mcp-server/ -v
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**A2A Servers:**
|
|
193
|
+
```bash
|
|
194
|
+
# Test utility agent with D402
|
|
195
|
+
uv run pytest automated-tests/agents/coingecko-utility-agent/ -v
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Summary
|
|
199
|
+
|
|
200
|
+
- **Use `starlette_middleware.py`** for MCP servers (backwards compatible)
|
|
201
|
+
- **Use `servers/starlette.py`** for A2A servers and new code (flexible, type-safe)
|
|
202
|
+
- Both middlewares share the same core payment validation logic
|
|
203
|
+
- Both support facilitator integration for settlement
|
|
204
|
+
- The generalized version is recommended for all new development
|
|
205
|
+
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# D402PriceBuilder - Simple Price Creation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`D402PriceBuilder` is a helper class that simplifies creating `TokenAmount` objects for D402 payment configuration. It works with **any token** - just initialize it with your token configuration.
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from traia_iatp.d402 import D402PriceBuilder
|
|
11
|
+
|
|
12
|
+
# Initialize with YOUR token configuration (any token!)
|
|
13
|
+
builder = D402PriceBuilder(
|
|
14
|
+
token_address="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", # Your token address
|
|
15
|
+
token_decimals=6, # Your token decimals
|
|
16
|
+
network="sepolia", # Your network
|
|
17
|
+
token_symbol="USDC" # Your token symbol
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Create prices easily
|
|
21
|
+
price_001 = builder.create_price(0.001) # $0.001
|
|
22
|
+
price_01 = builder.create_price(0.01) # $0.01
|
|
23
|
+
price_05 = builder.create_price(0.05) # $0.05
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Examples with Different Tokens
|
|
27
|
+
|
|
28
|
+
### USDC (6 decimals)
|
|
29
|
+
```python
|
|
30
|
+
usdc_builder = D402PriceBuilder(
|
|
31
|
+
token_address="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
32
|
+
token_decimals=6,
|
|
33
|
+
network="sepolia",
|
|
34
|
+
token_symbol="USDC"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
price = usdc_builder.create_price(0.01)
|
|
38
|
+
# Result: TokenAmount(amount="10000", ...) → 0.01 * 10^6 = 10000
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### TRAIA (18 decimals)
|
|
42
|
+
```python
|
|
43
|
+
traia_builder = D402PriceBuilder(
|
|
44
|
+
token_address="0xYourTraiaTokenAddress...",
|
|
45
|
+
token_decimals=18, # Standard ERC20
|
|
46
|
+
network="base-mainnet",
|
|
47
|
+
token_symbol="TRAIA"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
price = traia_builder.create_price(0.01)
|
|
51
|
+
# Result: TokenAmount(amount="10000000000000000", ...) → 0.01 * 10^18
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Any Custom Token
|
|
55
|
+
```python
|
|
56
|
+
custom_builder = D402PriceBuilder(
|
|
57
|
+
token_address="0xYourCustomToken...",
|
|
58
|
+
token_decimals=8, # Your token's decimals
|
|
59
|
+
network="arbitrum-mainnet",
|
|
60
|
+
token_symbol="CUSTOM"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
price = custom_builder.create_price(0.05)
|
|
64
|
+
# Result: TokenAmount(amount="5000000", ...) → 0.05 * 10^8 = 5000000
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage in Servers
|
|
68
|
+
|
|
69
|
+
### MCP Servers
|
|
70
|
+
```python
|
|
71
|
+
from traia_iatp.d402 import D402PriceBuilder
|
|
72
|
+
from traia_iatp.d402.servers.mcp import require_payment_for_tool
|
|
73
|
+
|
|
74
|
+
# Initialize builder from environment
|
|
75
|
+
builder = D402PriceBuilder(
|
|
76
|
+
token_address=os.getenv("TOKEN_ADDRESS"),
|
|
77
|
+
token_decimals=int(os.getenv("TOKEN_DECIMALS", "6")),
|
|
78
|
+
network=os.getenv("NETWORK", "sepolia"),
|
|
79
|
+
token_symbol=os.getenv("TOKEN_SYMBOL", "USDC")
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Use in decorators - clean and simple!
|
|
83
|
+
@mcp.tool()
|
|
84
|
+
@require_payment_for_tool(
|
|
85
|
+
price=builder.create_price(0.001), # $0.001
|
|
86
|
+
description="Quick check"
|
|
87
|
+
)
|
|
88
|
+
async def quick_check(context):
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
@mcp.tool()
|
|
92
|
+
@require_payment_for_tool(
|
|
93
|
+
price=builder.create_price(0.05), # $0.05 - different price!
|
|
94
|
+
description="Deep analysis"
|
|
95
|
+
)
|
|
96
|
+
async def deep_analysis(context):
|
|
97
|
+
pass
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### A2A Servers
|
|
101
|
+
```python
|
|
102
|
+
from traia_iatp.d402 import D402PriceBuilder
|
|
103
|
+
from traia_iatp.d402.servers.starlette import _build_payment_config
|
|
104
|
+
|
|
105
|
+
# Initialize builder from environment
|
|
106
|
+
builder = D402PriceBuilder(
|
|
107
|
+
token_address=os.getenv("D402_TOKEN_ADDRESS"),
|
|
108
|
+
token_decimals=int(os.getenv("D402_TOKEN_DECIMALS", "6")),
|
|
109
|
+
network=os.getenv("D402_NETWORK", "sepolia"),
|
|
110
|
+
token_symbol=os.getenv("D402_TOKEN_SYMBOL", "USDC")
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Create price
|
|
114
|
+
d402_token_amount = builder.create_price(float(os.getenv("D402_PRICE_USD", "0.01")))
|
|
115
|
+
|
|
116
|
+
# Build endpoint config
|
|
117
|
+
endpoint_payment_configs = {
|
|
118
|
+
"/": _build_payment_config(
|
|
119
|
+
price=d402_token_amount,
|
|
120
|
+
server_address=contract_address,
|
|
121
|
+
description="A2A request"
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### General FastAPI Servers
|
|
127
|
+
```python
|
|
128
|
+
from fastapi import FastAPI
|
|
129
|
+
from traia_iatp.d402 import D402PriceBuilder
|
|
130
|
+
from traia_iatp.d402.servers import require_payment, extract_payment_configs
|
|
131
|
+
|
|
132
|
+
app = FastAPI()
|
|
133
|
+
|
|
134
|
+
# Initialize builder from environment
|
|
135
|
+
builder = D402PriceBuilder(
|
|
136
|
+
token_address=os.getenv("TOKEN_ADDRESS"),
|
|
137
|
+
token_decimals=int(os.getenv("TOKEN_DECIMALS")),
|
|
138
|
+
network=os.getenv("NETWORK"),
|
|
139
|
+
token_symbol=os.getenv("TOKEN_SYMBOL")
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Use in decorators
|
|
143
|
+
@app.post("/analyze")
|
|
144
|
+
@require_payment(
|
|
145
|
+
price=builder.create_price(0.01),
|
|
146
|
+
endpoint_path="/analyze",
|
|
147
|
+
description="Analysis"
|
|
148
|
+
)
|
|
149
|
+
async def analyze():
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
@app.post("/premium")
|
|
153
|
+
@require_payment(
|
|
154
|
+
price=builder.create_price(0.10), # Different price!
|
|
155
|
+
endpoint_path="/premium",
|
|
156
|
+
description="Premium analysis"
|
|
157
|
+
)
|
|
158
|
+
async def premium():
|
|
159
|
+
pass
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Benefits
|
|
163
|
+
|
|
164
|
+
### Before (Manual TokenAmount creation)
|
|
165
|
+
```python
|
|
166
|
+
# Repetitive, error-prone
|
|
167
|
+
price1 = TokenAmount(
|
|
168
|
+
amount="10000",
|
|
169
|
+
asset=TokenAsset(
|
|
170
|
+
address="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
171
|
+
decimals=6,
|
|
172
|
+
network="sepolia",
|
|
173
|
+
symbol="USDC",
|
|
174
|
+
eip712=EIP712Domain(name="IATPWallet", version="1")
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
price2 = TokenAmount(
|
|
179
|
+
amount="50000", # Have to calculate: 0.05 * 10^6
|
|
180
|
+
asset=TokenAsset(
|
|
181
|
+
address="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", # Same config repeated!
|
|
182
|
+
decimals=6,
|
|
183
|
+
network="sepolia",
|
|
184
|
+
symbol="USDC",
|
|
185
|
+
eip712=EIP712Domain(name="IATPWallet", version="1")
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### After (With D402PriceBuilder)
|
|
191
|
+
```python
|
|
192
|
+
# Clean, simple, reusable
|
|
193
|
+
builder = D402PriceBuilder(
|
|
194
|
+
token_address="0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
195
|
+
token_decimals=6,
|
|
196
|
+
network="sepolia",
|
|
197
|
+
token_symbol="USDC"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
price1 = builder.create_price(0.01) # Simple!
|
|
201
|
+
price2 = builder.create_price(0.05) # No calculation needed!
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Methods
|
|
205
|
+
|
|
206
|
+
### `create_price(amount_usd: float) -> TokenAmount`
|
|
207
|
+
|
|
208
|
+
Create price from USD amount. Automatically converts to atomic units.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
builder = D402PriceBuilder(token_decimals=6, ...)
|
|
212
|
+
|
|
213
|
+
builder.create_price(0.01) # → "10000" wei (0.01 * 10^6)
|
|
214
|
+
builder.create_price(0.001) # → "1000" wei
|
|
215
|
+
builder.create_price(1.00) # → "1000000" wei
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### `create_price_wei(amount_wei: str) -> TokenAmount`
|
|
219
|
+
|
|
220
|
+
Create price from exact atomic units (when you don't want USD conversion).
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
builder = D402PriceBuilder(token_decimals=6, ...)
|
|
224
|
+
|
|
225
|
+
builder.create_price_wei("10000") # Exactly 10000 atomic units
|
|
226
|
+
builder.create_price_wei("1234") # Exactly 1234 atomic units
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Token Decimals Reference
|
|
230
|
+
|
|
231
|
+
| Token | Decimals | Example: $0.01 |
|
|
232
|
+
|-------|----------|----------------|
|
|
233
|
+
| USDC | 6 | 10,000 |
|
|
234
|
+
| DAI | 18 | 10,000,000,000,000,000 |
|
|
235
|
+
| TRAIA | 18 | 10,000,000,000,000,000 |
|
|
236
|
+
| WBTC | 8 | 1,000,000 |
|
|
237
|
+
|
|
238
|
+
The builder handles the conversion automatically - you just specify the USD amount!
|
|
239
|
+
|
|
240
|
+
## Summary
|
|
241
|
+
|
|
242
|
+
✅ **Generic** - Works with any token
|
|
243
|
+
✅ **Simple** - Initialize once, create many prices
|
|
244
|
+
✅ **Type-safe** - Returns proper `TokenAmount` objects
|
|
245
|
+
✅ **No calculation** - Automatically converts USD to atomic units
|
|
246
|
+
✅ **Reusable** - One builder for all your endpoints
|
|
247
|
+
|
|
248
|
+
**No more manual TokenAmount creation!**
|
|
249
|
+
|