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.
Files changed (95) hide show
  1. traia_iatp/__init__.py +105 -8
  2. traia_iatp/cli/main.py +85 -1
  3. traia_iatp/client/__init__.py +28 -3
  4. traia_iatp/client/crewai_a2a_tools.py +32 -12
  5. traia_iatp/client/d402_a2a_client.py +348 -0
  6. traia_iatp/contracts/__init__.py +11 -0
  7. traia_iatp/contracts/data/abis/contract-abis-localhost.json +4091 -0
  8. traia_iatp/contracts/data/abis/contract-abis-sepolia.json +4890 -0
  9. traia_iatp/contracts/data/addresses/contract-addresses.json +17 -0
  10. traia_iatp/contracts/data/addresses/contract-proxies.json +12 -0
  11. traia_iatp/contracts/iatp_contracts_config.py +263 -0
  12. traia_iatp/contracts/wallet_creator.py +369 -0
  13. traia_iatp/core/models.py +17 -3
  14. traia_iatp/d402/MIDDLEWARE_ARCHITECTURE.md +205 -0
  15. traia_iatp/d402/PRICE_BUILDER_USAGE.md +249 -0
  16. traia_iatp/d402/README.md +489 -0
  17. traia_iatp/d402/__init__.py +54 -0
  18. traia_iatp/d402/asgi_wrapper.py +469 -0
  19. traia_iatp/d402/chains.py +102 -0
  20. traia_iatp/d402/client.py +150 -0
  21. traia_iatp/d402/clients/__init__.py +7 -0
  22. traia_iatp/d402/clients/base.py +218 -0
  23. traia_iatp/d402/clients/httpx.py +266 -0
  24. traia_iatp/d402/common.py +114 -0
  25. traia_iatp/d402/encoding.py +28 -0
  26. traia_iatp/d402/examples/client_example.py +197 -0
  27. traia_iatp/d402/examples/server_example.py +171 -0
  28. traia_iatp/d402/facilitator.py +481 -0
  29. traia_iatp/d402/mcp_middleware.py +296 -0
  30. traia_iatp/d402/models.py +116 -0
  31. traia_iatp/d402/networks.py +98 -0
  32. traia_iatp/d402/path.py +43 -0
  33. traia_iatp/d402/payment_introspection.py +126 -0
  34. traia_iatp/d402/payment_signing.py +183 -0
  35. traia_iatp/d402/price_builder.py +164 -0
  36. traia_iatp/d402/servers/__init__.py +61 -0
  37. traia_iatp/d402/servers/base.py +139 -0
  38. traia_iatp/d402/servers/example_general_server.py +140 -0
  39. traia_iatp/d402/servers/fastapi.py +253 -0
  40. traia_iatp/d402/servers/mcp.py +304 -0
  41. traia_iatp/d402/servers/starlette.py +878 -0
  42. traia_iatp/d402/starlette_middleware.py +529 -0
  43. traia_iatp/d402/types.py +300 -0
  44. traia_iatp/mcp/D402_MCP_ADAPTER_FLOW.md +357 -0
  45. traia_iatp/mcp/__init__.py +3 -0
  46. traia_iatp/mcp/d402_mcp_tool_adapter.py +526 -0
  47. traia_iatp/mcp/mcp_agent_template.py +78 -13
  48. traia_iatp/mcp/templates/Dockerfile.j2 +27 -4
  49. traia_iatp/mcp/templates/README.md.j2 +104 -8
  50. traia_iatp/mcp/templates/cursor-rules.md.j2 +194 -0
  51. traia_iatp/mcp/templates/deployment_params.json.j2 +1 -2
  52. traia_iatp/mcp/templates/docker-compose.yml.j2 +13 -3
  53. traia_iatp/mcp/templates/env.example.j2 +60 -0
  54. traia_iatp/mcp/templates/mcp_health_check.py.j2 +2 -2
  55. traia_iatp/mcp/templates/pyproject.toml.j2 +11 -5
  56. traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
  57. traia_iatp/mcp/templates/run_local_docker.sh.j2 +320 -10
  58. traia_iatp/mcp/templates/server.py.j2 +174 -197
  59. traia_iatp/mcp/traia_mcp_adapter.py +182 -20
  60. traia_iatp/registry/__init__.py +47 -12
  61. traia_iatp/registry/atlas_search_indexes.json +108 -54
  62. traia_iatp/registry/iatp_search_api.py +169 -39
  63. traia_iatp/registry/mongodb_registry.py +241 -69
  64. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +1 -1
  65. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +8 -8
  66. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +1 -1
  67. traia_iatp/registry/readmes/README.md +3 -3
  68. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +6 -6
  69. traia_iatp/scripts/__init__.py +2 -0
  70. traia_iatp/scripts/create_wallet.py +244 -0
  71. traia_iatp/server/a2a_server.py +22 -7
  72. traia_iatp/server/iatp_server_template_generator.py +23 -0
  73. traia_iatp/server/templates/.dockerignore.j2 +48 -0
  74. traia_iatp/server/templates/Dockerfile.j2 +23 -1
  75. traia_iatp/server/templates/README.md +2 -2
  76. traia_iatp/server/templates/README.md.j2 +5 -5
  77. traia_iatp/server/templates/__main__.py.j2 +374 -66
  78. traia_iatp/server/templates/agent.py.j2 +12 -11
  79. traia_iatp/server/templates/agent_config.json.j2 +3 -3
  80. traia_iatp/server/templates/agent_executor.py.j2 +45 -27
  81. traia_iatp/server/templates/env.example.j2 +32 -4
  82. traia_iatp/server/templates/gitignore.j2 +7 -0
  83. traia_iatp/server/templates/pyproject.toml.j2 +13 -12
  84. traia_iatp/server/templates/run_local_docker.sh.j2 +143 -11
  85. traia_iatp/server/templates/server.py.j2 +197 -10
  86. traia_iatp/special_agencies/registry_search_agency.py +1 -1
  87. traia_iatp/utils/iatp_utils.py +6 -6
  88. traia_iatp-0.1.67.dist-info/METADATA +320 -0
  89. traia_iatp-0.1.67.dist-info/RECORD +117 -0
  90. traia_iatp-0.1.2.dist-info/METADATA +0 -414
  91. traia_iatp-0.1.2.dist-info/RECORD +0 -72
  92. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/WHEEL +0 -0
  93. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/entry_points.txt +0 -0
  94. {traia_iatp-0.1.2.dist-info → traia_iatp-0.1.67.dist-info}/licenses/LICENSE +0 -0
  95. {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 Config:
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 Config:
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 Config:
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
+