crewai-x402 0.1.0__tar.gz

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.
@@ -0,0 +1,48 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .env
25
+ .venv
26
+ env/
27
+ venv/
28
+ ENV/
29
+
30
+ # IDE
31
+ .idea/
32
+ .vscode/
33
+ *.swp
34
+ *.swo
35
+
36
+ # Testing
37
+ .pytest_cache/
38
+ .coverage
39
+ htmlcov/
40
+ .tox/
41
+ .nox/
42
+
43
+ # mypy
44
+ .mypy_cache/
45
+
46
+ # OS
47
+ .DS_Store
48
+ Thumbs.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentRails
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.4
2
+ Name: crewai-x402
3
+ Version: 0.1.0
4
+ Summary: x402 payment protocol integration for CrewAI - enable AI agents to pay for APIs with USDC
5
+ Project-URL: Homepage, https://agentrails.io
6
+ Project-URL: Documentation, https://agentrails.io/docs
7
+ Project-URL: Repository, https://github.com/kmatthewsio/crewai-x402
8
+ Project-URL: Issues, https://github.com/kmatthewsio/crewai-x402/issues
9
+ Author-email: AgentRails <dev@agentrails.io>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agents,api-monetization,crewai,crypto,payments,usdc,x402
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: crewai>=0.10.0
23
+ Requires-Dist: eth-account>=0.10.0
24
+ Requires-Dist: eth-typing>=3.0.0
25
+ Requires-Dist: httpx>=0.25.0
26
+ Requires-Dist: pydantic>=2.0.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
30
+ Requires-Dist: pytest-httpx>=0.22.0; extra == 'dev'
31
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # crewai-x402
36
+
37
+ [![PyPI version](https://badge.fury.io/py/crewai-x402.svg)](https://pypi.org/project/crewai-x402/)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
+
40
+ **Enable CrewAI agents to pay for APIs with USDC using the x402 protocol.**
41
+
42
+ crewai-x402 integrates the [x402 payment protocol](https://x402.org) with CrewAI, allowing your AI agent crews to autonomously access paid APIs without managing API keys or subscriptions.
43
+
44
+ ## What is x402?
45
+
46
+ x402 is the HTTP-native payment protocol that finally implements the `402 Payment Required` status code. Instead of API keys and monthly subscriptions, software pays software—per request, in USDC, with cryptographic proof.
47
+
48
+ **How it works:**
49
+ 1. Agent requests a resource
50
+ 2. Server returns `402 Payment Required` with price info
51
+ 3. Agent signs a USDC payment authorization (EIP-3009)
52
+ 4. Agent retries with payment proof
53
+ 5. Server settles on-chain, returns data
54
+
55
+ All in a single HTTP round-trip.
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install crewai-x402
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ import os
67
+ from crewai import Agent, Crew, Task
68
+ from crewai_x402 import X402Wallet, X402Tool
69
+
70
+ # 1. Create a wallet with a USDC budget
71
+ wallet = X402Wallet(
72
+ private_key=os.environ["WALLET_PRIVATE_KEY"],
73
+ network="base-mainnet",
74
+ budget_usd=10.00
75
+ )
76
+
77
+ # 2. Create the payment tool
78
+ payment_tool = X402Tool(wallet=wallet)
79
+
80
+ # 3. Add to your agent
81
+ researcher = Agent(
82
+ role="Research Analyst",
83
+ goal="Find accurate data using any available source",
84
+ backstory="You have access to both free and paid APIs.",
85
+ tools=[payment_tool],
86
+ )
87
+
88
+ # 4. Agent can now pay for premium data
89
+ task = Task(
90
+ description="Get premium market data from https://api.example.com/premium",
91
+ agent=researcher,
92
+ )
93
+
94
+ crew = Crew(agents=[researcher], tasks=[task])
95
+ result = crew.kickoff()
96
+ ```
97
+
98
+ ## Features
99
+
100
+ ### Automatic Payment Handling
101
+ The `X402Tool` automatically detects 402 responses and handles payment negotiation:
102
+
103
+ ```python
104
+ tool = X402Tool(
105
+ wallet=wallet,
106
+ auto_pay=True, # Automatically pay when within budget
107
+ timeout=30.0, # Request timeout in seconds
108
+ )
109
+ ```
110
+
111
+ ### Budget Control
112
+ Set spending limits at the wallet level:
113
+
114
+ ```python
115
+ wallet = X402Wallet(
116
+ private_key=key,
117
+ network="base-mainnet",
118
+ budget_usd=5.00 # Crew can't spend more than $5
119
+ )
120
+
121
+ # Check remaining budget
122
+ print(f"Remaining: ${wallet.remaining_usd}")
123
+
124
+ # Check if can afford a specific amount
125
+ if wallet.can_afford(0.01):
126
+ print("Can afford $0.01 request")
127
+ ```
128
+
129
+ ### Per-Request Price Limits
130
+ Limit how much an agent can pay for a single request:
131
+
132
+ ```python
133
+ # When calling the tool directly
134
+ result = tool._run(
135
+ url="https://api.example.com/expensive",
136
+ max_price_usd=0.05 # Won't pay more than $0.05 for this request
137
+ )
138
+ ```
139
+
140
+ ### Payment History
141
+ Track all payments made by the wallet:
142
+
143
+ ```python
144
+ for payment in wallet.payments:
145
+ print(f"{payment.resource_url}: ${payment.amount_usd}")
146
+
147
+ # Get summary
148
+ summary = wallet.get_payment_summary()
149
+ print(f"Total spent: ${summary['spent_usd']}")
150
+ print(f"Payments made: {summary['payment_count']}")
151
+ ```
152
+
153
+ ### Multi-Network Support
154
+ Supports multiple EVM networks:
155
+
156
+ ```python
157
+ # Base (recommended - low fees)
158
+ wallet = X402Wallet(private_key=key, network="base-mainnet")
159
+
160
+ # Ethereum
161
+ wallet = X402Wallet(private_key=key, network="ethereum-mainnet")
162
+
163
+ # Testnets
164
+ wallet = X402Wallet(private_key=key, network="base-sepolia")
165
+ wallet = X402Wallet(private_key=key, network="arc-testnet")
166
+ ```
167
+
168
+ ## API Reference
169
+
170
+ ### X402Wallet
171
+
172
+ ```python
173
+ X402Wallet(
174
+ private_key: str, # Hex-encoded private key
175
+ network: str, # Network name (e.g., "base-mainnet")
176
+ budget_usd: float, # Maximum USD to spend
177
+ )
178
+ ```
179
+
180
+ **Properties:**
181
+ - `address` - Wallet address
182
+ - `spent_usd` - Total USD spent
183
+ - `remaining_usd` - Remaining budget
184
+ - `payments` - List of PaymentRecord objects
185
+
186
+ **Methods:**
187
+ - `can_afford(amount_usd)` - Check if budget allows payment
188
+ - `sign_payment(to, amount, valid_before)` - Sign EIP-3009 authorization
189
+ - `get_payment_summary()` - Get spending summary dict
190
+ - `reset_budget(new_budget)` - Reset budget and clear history
191
+
192
+ ### X402Tool
193
+
194
+ ```python
195
+ X402Tool(
196
+ wallet: X402Wallet, # Wallet for payments
197
+ auto_pay: bool = True, # Auto-pay when within budget
198
+ timeout: float = 30.0, # HTTP timeout
199
+ )
200
+ ```
201
+
202
+ **Tool Input Fields:**
203
+ - `url` (required) - URL to request
204
+ - `method` - HTTP method (default: "GET")
205
+ - `body` - Request body
206
+ - `headers` - Additional headers
207
+ - `max_price_usd` - Per-request price limit
208
+
209
+ ## Networks
210
+
211
+ | Network | Chain ID | Environment |
212
+ |---------|----------|-------------|
213
+ | `base-mainnet` | 8453 | Production |
214
+ | `base-sepolia` | 84532 | Testnet |
215
+ | `ethereum-mainnet` | 1 | Production |
216
+ | `ethereum-sepolia` | 11155111 | Testnet |
217
+ | `arc-testnet` | 5042002 | Testnet |
218
+
219
+ ## Security Considerations
220
+
221
+ 1. **Never commit private keys** - Use environment variables or secret managers
222
+ 2. **Set appropriate budgets** - Limit what crews can spend
223
+ 3. **Use testnets first** - Test with `base-sepolia` before mainnet
224
+ 4. **Monitor spending** - Check `wallet.get_payment_summary()` regularly
225
+
226
+ ## Examples
227
+
228
+ See the [examples/](examples/) directory:
229
+ - `research_crew.py` - Research crew with payment capability
230
+
231
+ ## How It Differs From API Keys
232
+
233
+ | API Keys | x402 |
234
+ |----------|------|
235
+ | 1 key per service | 1 wallet for all services |
236
+ | Monthly subscriptions | Pay per request |
237
+ | Human signup required | Zero onboarding |
238
+ | Credential rotation | No credentials to leak |
239
+ | Service-level limits | Agent-level budgets |
240
+
241
+ ## Related Packages
242
+
243
+ - [langchain-x402](https://pypi.org/project/langchain-x402/) - x402 integration for LangChain
244
+
245
+ ## Resources
246
+
247
+ - [x402 Protocol Spec](https://x402.org)
248
+ - [AgentRails Documentation](https://agentrails.io/docs)
249
+ - [EIP-3009 Specification](https://eips.ethereum.org/EIPS/eip-3009)
250
+ - [CrewAI Documentation](https://docs.crewai.com)
251
+
252
+ ## License
253
+
254
+ MIT License - see [LICENSE](LICENSE) for details.
255
+
256
+ ## Contributing
257
+
258
+ Contributions welcome! Please read our contributing guidelines and submit PRs to the [GitHub repository](https://github.com/kmatthewsio/crewai-x402).
@@ -0,0 +1,224 @@
1
+ # crewai-x402
2
+
3
+ [![PyPI version](https://badge.fury.io/py/crewai-x402.svg)](https://pypi.org/project/crewai-x402/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ **Enable CrewAI agents to pay for APIs with USDC using the x402 protocol.**
7
+
8
+ crewai-x402 integrates the [x402 payment protocol](https://x402.org) with CrewAI, allowing your AI agent crews to autonomously access paid APIs without managing API keys or subscriptions.
9
+
10
+ ## What is x402?
11
+
12
+ x402 is the HTTP-native payment protocol that finally implements the `402 Payment Required` status code. Instead of API keys and monthly subscriptions, software pays software—per request, in USDC, with cryptographic proof.
13
+
14
+ **How it works:**
15
+ 1. Agent requests a resource
16
+ 2. Server returns `402 Payment Required` with price info
17
+ 3. Agent signs a USDC payment authorization (EIP-3009)
18
+ 4. Agent retries with payment proof
19
+ 5. Server settles on-chain, returns data
20
+
21
+ All in a single HTTP round-trip.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install crewai-x402
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```python
32
+ import os
33
+ from crewai import Agent, Crew, Task
34
+ from crewai_x402 import X402Wallet, X402Tool
35
+
36
+ # 1. Create a wallet with a USDC budget
37
+ wallet = X402Wallet(
38
+ private_key=os.environ["WALLET_PRIVATE_KEY"],
39
+ network="base-mainnet",
40
+ budget_usd=10.00
41
+ )
42
+
43
+ # 2. Create the payment tool
44
+ payment_tool = X402Tool(wallet=wallet)
45
+
46
+ # 3. Add to your agent
47
+ researcher = Agent(
48
+ role="Research Analyst",
49
+ goal="Find accurate data using any available source",
50
+ backstory="You have access to both free and paid APIs.",
51
+ tools=[payment_tool],
52
+ )
53
+
54
+ # 4. Agent can now pay for premium data
55
+ task = Task(
56
+ description="Get premium market data from https://api.example.com/premium",
57
+ agent=researcher,
58
+ )
59
+
60
+ crew = Crew(agents=[researcher], tasks=[task])
61
+ result = crew.kickoff()
62
+ ```
63
+
64
+ ## Features
65
+
66
+ ### Automatic Payment Handling
67
+ The `X402Tool` automatically detects 402 responses and handles payment negotiation:
68
+
69
+ ```python
70
+ tool = X402Tool(
71
+ wallet=wallet,
72
+ auto_pay=True, # Automatically pay when within budget
73
+ timeout=30.0, # Request timeout in seconds
74
+ )
75
+ ```
76
+
77
+ ### Budget Control
78
+ Set spending limits at the wallet level:
79
+
80
+ ```python
81
+ wallet = X402Wallet(
82
+ private_key=key,
83
+ network="base-mainnet",
84
+ budget_usd=5.00 # Crew can't spend more than $5
85
+ )
86
+
87
+ # Check remaining budget
88
+ print(f"Remaining: ${wallet.remaining_usd}")
89
+
90
+ # Check if can afford a specific amount
91
+ if wallet.can_afford(0.01):
92
+ print("Can afford $0.01 request")
93
+ ```
94
+
95
+ ### Per-Request Price Limits
96
+ Limit how much an agent can pay for a single request:
97
+
98
+ ```python
99
+ # When calling the tool directly
100
+ result = tool._run(
101
+ url="https://api.example.com/expensive",
102
+ max_price_usd=0.05 # Won't pay more than $0.05 for this request
103
+ )
104
+ ```
105
+
106
+ ### Payment History
107
+ Track all payments made by the wallet:
108
+
109
+ ```python
110
+ for payment in wallet.payments:
111
+ print(f"{payment.resource_url}: ${payment.amount_usd}")
112
+
113
+ # Get summary
114
+ summary = wallet.get_payment_summary()
115
+ print(f"Total spent: ${summary['spent_usd']}")
116
+ print(f"Payments made: {summary['payment_count']}")
117
+ ```
118
+
119
+ ### Multi-Network Support
120
+ Supports multiple EVM networks:
121
+
122
+ ```python
123
+ # Base (recommended - low fees)
124
+ wallet = X402Wallet(private_key=key, network="base-mainnet")
125
+
126
+ # Ethereum
127
+ wallet = X402Wallet(private_key=key, network="ethereum-mainnet")
128
+
129
+ # Testnets
130
+ wallet = X402Wallet(private_key=key, network="base-sepolia")
131
+ wallet = X402Wallet(private_key=key, network="arc-testnet")
132
+ ```
133
+
134
+ ## API Reference
135
+
136
+ ### X402Wallet
137
+
138
+ ```python
139
+ X402Wallet(
140
+ private_key: str, # Hex-encoded private key
141
+ network: str, # Network name (e.g., "base-mainnet")
142
+ budget_usd: float, # Maximum USD to spend
143
+ )
144
+ ```
145
+
146
+ **Properties:**
147
+ - `address` - Wallet address
148
+ - `spent_usd` - Total USD spent
149
+ - `remaining_usd` - Remaining budget
150
+ - `payments` - List of PaymentRecord objects
151
+
152
+ **Methods:**
153
+ - `can_afford(amount_usd)` - Check if budget allows payment
154
+ - `sign_payment(to, amount, valid_before)` - Sign EIP-3009 authorization
155
+ - `get_payment_summary()` - Get spending summary dict
156
+ - `reset_budget(new_budget)` - Reset budget and clear history
157
+
158
+ ### X402Tool
159
+
160
+ ```python
161
+ X402Tool(
162
+ wallet: X402Wallet, # Wallet for payments
163
+ auto_pay: bool = True, # Auto-pay when within budget
164
+ timeout: float = 30.0, # HTTP timeout
165
+ )
166
+ ```
167
+
168
+ **Tool Input Fields:**
169
+ - `url` (required) - URL to request
170
+ - `method` - HTTP method (default: "GET")
171
+ - `body` - Request body
172
+ - `headers` - Additional headers
173
+ - `max_price_usd` - Per-request price limit
174
+
175
+ ## Networks
176
+
177
+ | Network | Chain ID | Environment |
178
+ |---------|----------|-------------|
179
+ | `base-mainnet` | 8453 | Production |
180
+ | `base-sepolia` | 84532 | Testnet |
181
+ | `ethereum-mainnet` | 1 | Production |
182
+ | `ethereum-sepolia` | 11155111 | Testnet |
183
+ | `arc-testnet` | 5042002 | Testnet |
184
+
185
+ ## Security Considerations
186
+
187
+ 1. **Never commit private keys** - Use environment variables or secret managers
188
+ 2. **Set appropriate budgets** - Limit what crews can spend
189
+ 3. **Use testnets first** - Test with `base-sepolia` before mainnet
190
+ 4. **Monitor spending** - Check `wallet.get_payment_summary()` regularly
191
+
192
+ ## Examples
193
+
194
+ See the [examples/](examples/) directory:
195
+ - `research_crew.py` - Research crew with payment capability
196
+
197
+ ## How It Differs From API Keys
198
+
199
+ | API Keys | x402 |
200
+ |----------|------|
201
+ | 1 key per service | 1 wallet for all services |
202
+ | Monthly subscriptions | Pay per request |
203
+ | Human signup required | Zero onboarding |
204
+ | Credential rotation | No credentials to leak |
205
+ | Service-level limits | Agent-level budgets |
206
+
207
+ ## Related Packages
208
+
209
+ - [langchain-x402](https://pypi.org/project/langchain-x402/) - x402 integration for LangChain
210
+
211
+ ## Resources
212
+
213
+ - [x402 Protocol Spec](https://x402.org)
214
+ - [AgentRails Documentation](https://agentrails.io/docs)
215
+ - [EIP-3009 Specification](https://eips.ethereum.org/EIPS/eip-3009)
216
+ - [CrewAI Documentation](https://docs.crewai.com)
217
+
218
+ ## License
219
+
220
+ MIT License - see [LICENSE](LICENSE) for details.
221
+
222
+ ## Contributing
223
+
224
+ Contributions welcome! Please read our contributing guidelines and submit PRs to the [GitHub repository](https://github.com/kmatthewsio/crewai-x402).
@@ -0,0 +1,19 @@
1
+ """CrewAI x402 integration - enable AI agents to pay for APIs with USDC."""
2
+
3
+ from .wallet import PaymentRecord, X402Wallet
4
+
5
+ __all__ = [
6
+ "X402Wallet",
7
+ "PaymentRecord",
8
+ ]
9
+
10
+ # Import tool only if crewai is available
11
+ try:
12
+ from .tool import X402Tool, X402ToolInput
13
+
14
+ __all__.extend(["X402Tool", "X402ToolInput"])
15
+ except ImportError:
16
+ # crewai not installed - tool not available
17
+ pass
18
+
19
+ __version__ = "0.1.0"
@@ -0,0 +1,125 @@
1
+ """EIP-3009 signature utilities for x402 payments."""
2
+
3
+ from typing import Any
4
+
5
+ from eth_account import Account
6
+
7
+
8
+ # Network configurations
9
+ NETWORKS: dict[str, dict[str, Any]] = {
10
+ "base-mainnet": {
11
+ "chain_id": 8453,
12
+ "usdc_address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
13
+ "name": "USD Coin",
14
+ "version": "2",
15
+ },
16
+ "base-sepolia": {
17
+ "chain_id": 84532,
18
+ "usdc_address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
19
+ "name": "USD Coin",
20
+ "version": "2",
21
+ },
22
+ "ethereum-mainnet": {
23
+ "chain_id": 1,
24
+ "usdc_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
25
+ "name": "USD Coin",
26
+ "version": "2",
27
+ },
28
+ "ethereum-sepolia": {
29
+ "chain_id": 11155111,
30
+ "usdc_address": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
31
+ "name": "USD Coin",
32
+ "version": "2",
33
+ },
34
+ "arc-testnet": {
35
+ "chain_id": 5042002,
36
+ "usdc_address": "0x3600000000000000000000000000000000000000",
37
+ "name": "USD Coin",
38
+ "version": "2",
39
+ },
40
+ }
41
+
42
+
43
+ def build_transfer_authorization_typed_data(
44
+ network: str,
45
+ from_address: str,
46
+ to_address: str,
47
+ value: int,
48
+ valid_after: int,
49
+ valid_before: int,
50
+ nonce: bytes,
51
+ ) -> dict[str, Any]:
52
+ """Build EIP-712 typed data for TransferWithAuthorization."""
53
+ config = NETWORKS.get(network)
54
+ if not config:
55
+ raise ValueError(f"Unknown network: {network}")
56
+
57
+ return {
58
+ "types": {
59
+ "EIP712Domain": [
60
+ {"name": "name", "type": "string"},
61
+ {"name": "version", "type": "string"},
62
+ {"name": "chainId", "type": "uint256"},
63
+ {"name": "verifyingContract", "type": "address"},
64
+ ],
65
+ "TransferWithAuthorization": [
66
+ {"name": "from", "type": "address"},
67
+ {"name": "to", "type": "address"},
68
+ {"name": "value", "type": "uint256"},
69
+ {"name": "validAfter", "type": "uint256"},
70
+ {"name": "validBefore", "type": "uint256"},
71
+ {"name": "nonce", "type": "bytes32"},
72
+ ],
73
+ },
74
+ "primaryType": "TransferWithAuthorization",
75
+ "domain": {
76
+ "name": config["name"],
77
+ "version": config["version"],
78
+ "chainId": config["chain_id"],
79
+ "verifyingContract": config["usdc_address"],
80
+ },
81
+ "message": {
82
+ "from": from_address,
83
+ "to": to_address,
84
+ "value": value,
85
+ "validAfter": valid_after,
86
+ "validBefore": valid_before,
87
+ "nonce": nonce if isinstance(nonce, bytes) else bytes.fromhex(nonce.replace("0x", "")),
88
+ },
89
+ }
90
+
91
+
92
+ def sign_transfer_authorization(
93
+ private_key: str,
94
+ network: str,
95
+ from_address: str,
96
+ to_address: str,
97
+ value: int,
98
+ valid_after: int,
99
+ valid_before: int,
100
+ nonce: bytes,
101
+ ) -> str:
102
+ """Sign a TransferWithAuthorization message and return the signature."""
103
+ typed_data = build_transfer_authorization_typed_data(
104
+ network=network,
105
+ from_address=from_address,
106
+ to_address=to_address,
107
+ value=value,
108
+ valid_after=valid_after,
109
+ valid_before=valid_before,
110
+ nonce=nonce,
111
+ )
112
+
113
+ # Sign the message - pass types without EIP712Domain (library adds it)
114
+ message_types = {
115
+ k: v for k, v in typed_data["types"].items() if k != "EIP712Domain"
116
+ }
117
+
118
+ account = Account.from_key(private_key)
119
+ signed = account.sign_typed_data(
120
+ typed_data["domain"],
121
+ message_types,
122
+ typed_data["message"],
123
+ )
124
+
125
+ return "0x" + signed.signature.hex()