arczk-agent 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,251 @@
1
+ Metadata-Version: 2.4
2
+ Name: arczk-agent
3
+ Version: 0.1.0
4
+ Summary: Autonomous AI Agent SDK for ArcNano Shielded Zero-Knowledge Nanopayments on Arc
5
+ Author-email: Trymbak Mahant <trymbakmahant@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Trymbakmahant/arcnano
8
+ Project-URL: Documentation, https://github.com/Trymbakmahant/arcnano
9
+ Project-URL: Repository, https://github.com/Trymbakmahant/arcnano
10
+ Project-URL: Bug Tracker, https://github.com/Trymbakmahant/arcnano/issues
11
+ Keywords: arcnano,zk-proofs,x402,ai-agents,langchain,crewai,micropayments,arc-network,groth16,zero-knowledge,autonomous-agents
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Security :: Cryptography
21
+ Classifier: Operating System :: OS Independent
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: requests>=2.28.0
25
+ Requires-Dist: httpx>=0.24.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
28
+ Requires-Dist: black>=23.0.0; extra == "dev"
29
+ Requires-Dist: twine>=4.0.0; extra == "dev"
30
+ Provides-Extra: langchain
31
+ Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
32
+ Provides-Extra: crewai
33
+ Requires-Dist: crewai>=0.1.0; extra == "crewai"
34
+
35
+ # arczk-agent
36
+
37
+ > **Autonomous AI Agent SDK for ArcNano Shielded Zero-Knowledge Nanopayments on Arc.**
38
+
39
+ [![PyPI version](https://img.shields.io/pypi/v/arczk-agent.svg?color=emerald)](https://pypi.org/project/arczk-agent/)
40
+ [![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://pypi.org/project/arczk-agent/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+ [![Arc Testnet](https://img.shields.io/badge/Arc_Testnet-5042002-blue.svg)](https://testnet.arcscan.io)
43
+ [![Latency](https://img.shields.io/badge/ZK_Proof_Gen-<10ms-brightgreen.svg)](https://github.com/Trymbakmahant/arcnano)
44
+
45
+ ---
46
+
47
+ ## ⚡ Overview
48
+
49
+ `arczk-agent` empowers autonomous AI agents (**LangChain**, **CrewAI**, **AutoGPT**, **LlamaIndex**) to execute instant, privacy-preserving micro-payments over standard **HTTP 402 Payment Required** protocols with complete identity unlinkability on the **Arc Network**.
50
+
51
+ ### Why use `arczk-agent`?
52
+
53
+ - 🛡️ **Zero Signer Exposure**: The calling AI agent never broadcasts on-chain transactions or exposes private keys. API providers cannot link prompt requests to any on-chain wallet balance.
54
+ - ⚡ **Autonomous X402 Negotiation**: Automatically intercepts `402 Payment Required` responses, extracts cryptographic invoice parameters, generates Groth16 spend proofs in RAM, and retries in single-digit milliseconds.
55
+ - 🔐 **Shielded Note Vault**: Client-side note generation (`secret`, `nullifier_seed`, Poseidon commitments) with local encrypted JSON persistence.
56
+ - 🤖 **Agent Framework Ready**: Drop-in tool adapters for **LangChain** and **CrewAI** multi-agent pipelines.
57
+ - ⛽ **Zero Gas Overhead**: Note settlement is batched and sponsored by API providers via Circle Gas Station (ERC-4337) on Arc L1.
58
+
59
+ ---
60
+
61
+ ## 🔄 How Autonomous X402 Negotiation Works
62
+
63
+ ```
64
+ Autonomous Agent (Python) Protected API Gateway (X402) Arc L1
65
+ | | |
66
+ | 1. POST /v1/inference (no credentials) | |
67
+ |--------------------------------------------->| |
68
+ | | |
69
+ | 2. 402 Payment Required | |
70
+ | WWW-Authenticate: X402 pool=... | |
71
+ |<---------------------------------------------| |
72
+ | | |
73
+ | -- Parse X402 Invoice | |
74
+ | -- Retrieve Note from Vault | |
75
+ | -- Generate Groth16 Proof in RAM | |
76
+ | | |
77
+ | 3. POST /v1/inference | |
78
+ | Authorization: X402 {"proof": ...} | |
79
+ |--------------------------------------------->| |
80
+ | |-- Verify Proof (<8ms) |
81
+ | |-- Check Nullifier Cache |
82
+ | 4. 200 OK (Stream LLM Response) | |
83
+ |<---------------------------------------------| |
84
+ | | 5. batchSpend (Sponsored) |
85
+ | -- Mark Note Spent in Vault |---------------------------->|
86
+ ```
87
+
88
+ ---
89
+
90
+ ## 📦 Installation
91
+
92
+ ```bash
93
+ # Standard installation
94
+ pip install arczk-agent
95
+
96
+ # Using uv (recommended for ultra-fast installs)
97
+ uv pip install arczk-agent
98
+
99
+ # With LangChain support
100
+ pip install "arczk-agent[langchain]"
101
+
102
+ # With CrewAI support
103
+ pip install "arczk-agent[crewai]"
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 🚀 Quickstart
109
+
110
+ ### 1. Direct Python Client with Transparent X402 Handling
111
+
112
+ The `ArcAgentClient` behaves like standard `requests`, but transparently intercepts HTTP 402 challenges and fulfills them:
113
+
114
+ ```python
115
+ from arczk import ArcAgentClient, NoteVault
116
+
117
+ # 1. Initialize local shielded note vault
118
+ vault = NoteVault("~/.arcnano/agent_vault.json")
119
+
120
+ # 2. Fund/initialize an unspent note (0.01 USDC = 10,000 micro-units)
121
+ vault.create_note(denomination=10000)
122
+
123
+ print(f"Active unspent balance: {vault.unspent_balance()} micro-units")
124
+
125
+ # 3. Create autonomous agent HTTP client
126
+ client = ArcAgentClient(vault=vault)
127
+
128
+ # 4. Request any X402-enabled API endpoint:
129
+ # The client automatically handles HTTP 402, generates a ZK proof, and fulfills payment!
130
+ response = client.post(
131
+ "https://api.inference.ai/v1/generate",
132
+ json={"prompt": "Analyze autonomous financial markets on Arc"}
133
+ )
134
+
135
+ print("Status:", response.status_code)
136
+ print("Response:", response.json())
137
+ ```
138
+
139
+ ---
140
+
141
+ ### 2. LangChain Autonomous Agent Integration
142
+
143
+ Plug `ArcNanoLangChainTool` into any LangChain agent executor:
144
+
145
+ ```python
146
+ from langchain.agents import AgentExecutor, create_openai_tools_agent
147
+ from langchain_openai import ChatOpenAI
148
+ from langchain_core.prompts import ChatPromptTemplate
149
+ from arczk.integrations import ArcNanoLangChainTool
150
+
151
+ # Initialize tool
152
+ payment_tool = ArcNanoLangChainTool()
153
+
154
+ tools = [payment_tool]
155
+ llm = ChatOpenAI(model="gpt-4o", temperature=0)
156
+
157
+ prompt = ChatPromptTemplate.from_messages([
158
+ ("system", "You are an autonomous research agent. Use the arcnano tool to pay for external compute APIs."),
159
+ ("human", "{input}"),
160
+ ("placeholder", "{agent_scratchpad}"),
161
+ ])
162
+
163
+ agent = create_openai_tools_agent(llm, tools, prompt)
164
+ agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
165
+
166
+ agent_executor.invoke({
167
+ "input": "Call https://api.inference.ai/v1/generate to retrieve specialized market intelligence."
168
+ })
169
+ ```
170
+
171
+ ---
172
+
173
+ ### 3. CrewAI Autonomous Agent Tool
174
+
175
+ ```python
176
+ from crewai import Agent, Task, Crew
177
+ from arczk.integrations import ArcNanoPaymentTool
178
+
179
+ payment_tool = ArcNanoPaymentTool()
180
+
181
+ researcher = Agent(
182
+ role="Autonomous Intelligence Scout",
183
+ goal="Gather paid API data on Arc without leaking signer identity",
184
+ backstory="Specialized in zero-knowledge agent transactions",
185
+ tools=[payment_tool.run],
186
+ verbose=True,
187
+ )
188
+
189
+ task = Task(
190
+ description="Fetch data from https://api.inference.ai/v1/generate using shielded payment.",
191
+ expected_output="JSON data returned from the paid inference endpoint.",
192
+ agent=researcher,
193
+ )
194
+
195
+ crew = Crew(agents=[researcher], tasks=[task])
196
+ result = crew.kickoff()
197
+ print(result)
198
+ ```
199
+
200
+ ---
201
+
202
+ ## 📚 Core Modules Reference
203
+
204
+ ### `ArcAgentClient`
205
+ Autonomous HTTP client with session pooling and automated payment retry:
206
+ - `client.request(method, url, headers=None, auto_pay=True, **kwargs)`
207
+ - `client.get(url, **kwargs)`
208
+ - `client.post(url, **kwargs)`
209
+ - `client.parse_invoice(response)`: Extracts `recipient`, `pool`, `root`, and `denomination` from RFC `WWW-Authenticate` headers or JSON body.
210
+
211
+ ### `NoteVault`
212
+ Encrypted local note manager storing client-side shielded notes:
213
+ - `vault.create_note(denomination=10000)`: Generates cryptographically secure random `secret` and `nullifier_seed` scalars in the BN254 field and computes Poseidon commitments.
214
+ - `vault.get_unspent_note(denomination=None)`: Retrieves the first unspent note with sufficient value.
215
+ - `vault.mark_spent(nullifier_hash)`: Marks note as consumed to prevent client-side replays.
216
+ - `vault.unspent_balance()`: Total available unspent units.
217
+ - `vault.save(filepath)` / `vault.load(filepath)`: JSON file persistence.
218
+
219
+ ### `Groth16Prover`
220
+ RAM-based Groth16 spend proof generator:
221
+ - Binds public signals `[root, nullifierHash, recipient, aspRoot]` to the provider's wallet.
222
+ - Prevents frontrunning: proof is cryptographically tied to the exact recipient address specified in the invoice.
223
+
224
+ ---
225
+
226
+ ## 🌐 Network & Contract Reference
227
+
228
+ | Parameter | Value |
229
+ | :--- | :--- |
230
+ | **Network** | Arc Testnet (Circle L1) |
231
+ | **Chain ID** | `5042002` |
232
+ | **Gas & Settlement Asset** | USDC (Native Gas & Denomination) |
233
+ | **RPC Endpoint** | `https://rpc.testnet.arc.network` |
234
+ | **Explorer** | [https://testnet.arcscan.io](https://testnet.arcscan.io) |
235
+ | **ArcNanoPool Contract** | [`0xa40d68FDEa3B6fb01c966A9d29A6fc341AE476Ca`](https://testnet.arcscan.io/address/0xa40d68FDEa3B6fb01c966A9d29A6fc341AE476Ca) |
236
+ | **Standard Denominations** | `1000` (0.001 USDC), `10000` (0.01 USDC), `100000` (0.1 USDC) |
237
+
238
+ ---
239
+
240
+ ## 🧪 Testing
241
+
242
+ ```bash
243
+ # Run test suite
244
+ pytest tests/ -v
245
+ ```
246
+
247
+ ---
248
+
249
+ ## 📄 License
250
+
251
+ MIT © [Trymbak Mahant](https://github.com/Trymbakmahant)
@@ -0,0 +1,217 @@
1
+ # arczk-agent
2
+
3
+ > **Autonomous AI Agent SDK for ArcNano Shielded Zero-Knowledge Nanopayments on Arc.**
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/arczk-agent.svg?color=emerald)](https://pypi.org/project/arczk-agent/)
6
+ [![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://pypi.org/project/arczk-agent/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Arc Testnet](https://img.shields.io/badge/Arc_Testnet-5042002-blue.svg)](https://testnet.arcscan.io)
9
+ [![Latency](https://img.shields.io/badge/ZK_Proof_Gen-<10ms-brightgreen.svg)](https://github.com/Trymbakmahant/arcnano)
10
+
11
+ ---
12
+
13
+ ## ⚡ Overview
14
+
15
+ `arczk-agent` empowers autonomous AI agents (**LangChain**, **CrewAI**, **AutoGPT**, **LlamaIndex**) to execute instant, privacy-preserving micro-payments over standard **HTTP 402 Payment Required** protocols with complete identity unlinkability on the **Arc Network**.
16
+
17
+ ### Why use `arczk-agent`?
18
+
19
+ - 🛡️ **Zero Signer Exposure**: The calling AI agent never broadcasts on-chain transactions or exposes private keys. API providers cannot link prompt requests to any on-chain wallet balance.
20
+ - ⚡ **Autonomous X402 Negotiation**: Automatically intercepts `402 Payment Required` responses, extracts cryptographic invoice parameters, generates Groth16 spend proofs in RAM, and retries in single-digit milliseconds.
21
+ - 🔐 **Shielded Note Vault**: Client-side note generation (`secret`, `nullifier_seed`, Poseidon commitments) with local encrypted JSON persistence.
22
+ - 🤖 **Agent Framework Ready**: Drop-in tool adapters for **LangChain** and **CrewAI** multi-agent pipelines.
23
+ - ⛽ **Zero Gas Overhead**: Note settlement is batched and sponsored by API providers via Circle Gas Station (ERC-4337) on Arc L1.
24
+
25
+ ---
26
+
27
+ ## 🔄 How Autonomous X402 Negotiation Works
28
+
29
+ ```
30
+ Autonomous Agent (Python) Protected API Gateway (X402) Arc L1
31
+ | | |
32
+ | 1. POST /v1/inference (no credentials) | |
33
+ |--------------------------------------------->| |
34
+ | | |
35
+ | 2. 402 Payment Required | |
36
+ | WWW-Authenticate: X402 pool=... | |
37
+ |<---------------------------------------------| |
38
+ | | |
39
+ | -- Parse X402 Invoice | |
40
+ | -- Retrieve Note from Vault | |
41
+ | -- Generate Groth16 Proof in RAM | |
42
+ | | |
43
+ | 3. POST /v1/inference | |
44
+ | Authorization: X402 {"proof": ...} | |
45
+ |--------------------------------------------->| |
46
+ | |-- Verify Proof (<8ms) |
47
+ | |-- Check Nullifier Cache |
48
+ | 4. 200 OK (Stream LLM Response) | |
49
+ |<---------------------------------------------| |
50
+ | | 5. batchSpend (Sponsored) |
51
+ | -- Mark Note Spent in Vault |---------------------------->|
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 📦 Installation
57
+
58
+ ```bash
59
+ # Standard installation
60
+ pip install arczk-agent
61
+
62
+ # Using uv (recommended for ultra-fast installs)
63
+ uv pip install arczk-agent
64
+
65
+ # With LangChain support
66
+ pip install "arczk-agent[langchain]"
67
+
68
+ # With CrewAI support
69
+ pip install "arczk-agent[crewai]"
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 🚀 Quickstart
75
+
76
+ ### 1. Direct Python Client with Transparent X402 Handling
77
+
78
+ The `ArcAgentClient` behaves like standard `requests`, but transparently intercepts HTTP 402 challenges and fulfills them:
79
+
80
+ ```python
81
+ from arczk import ArcAgentClient, NoteVault
82
+
83
+ # 1. Initialize local shielded note vault
84
+ vault = NoteVault("~/.arcnano/agent_vault.json")
85
+
86
+ # 2. Fund/initialize an unspent note (0.01 USDC = 10,000 micro-units)
87
+ vault.create_note(denomination=10000)
88
+
89
+ print(f"Active unspent balance: {vault.unspent_balance()} micro-units")
90
+
91
+ # 3. Create autonomous agent HTTP client
92
+ client = ArcAgentClient(vault=vault)
93
+
94
+ # 4. Request any X402-enabled API endpoint:
95
+ # The client automatically handles HTTP 402, generates a ZK proof, and fulfills payment!
96
+ response = client.post(
97
+ "https://api.inference.ai/v1/generate",
98
+ json={"prompt": "Analyze autonomous financial markets on Arc"}
99
+ )
100
+
101
+ print("Status:", response.status_code)
102
+ print("Response:", response.json())
103
+ ```
104
+
105
+ ---
106
+
107
+ ### 2. LangChain Autonomous Agent Integration
108
+
109
+ Plug `ArcNanoLangChainTool` into any LangChain agent executor:
110
+
111
+ ```python
112
+ from langchain.agents import AgentExecutor, create_openai_tools_agent
113
+ from langchain_openai import ChatOpenAI
114
+ from langchain_core.prompts import ChatPromptTemplate
115
+ from arczk.integrations import ArcNanoLangChainTool
116
+
117
+ # Initialize tool
118
+ payment_tool = ArcNanoLangChainTool()
119
+
120
+ tools = [payment_tool]
121
+ llm = ChatOpenAI(model="gpt-4o", temperature=0)
122
+
123
+ prompt = ChatPromptTemplate.from_messages([
124
+ ("system", "You are an autonomous research agent. Use the arcnano tool to pay for external compute APIs."),
125
+ ("human", "{input}"),
126
+ ("placeholder", "{agent_scratchpad}"),
127
+ ])
128
+
129
+ agent = create_openai_tools_agent(llm, tools, prompt)
130
+ agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
131
+
132
+ agent_executor.invoke({
133
+ "input": "Call https://api.inference.ai/v1/generate to retrieve specialized market intelligence."
134
+ })
135
+ ```
136
+
137
+ ---
138
+
139
+ ### 3. CrewAI Autonomous Agent Tool
140
+
141
+ ```python
142
+ from crewai import Agent, Task, Crew
143
+ from arczk.integrations import ArcNanoPaymentTool
144
+
145
+ payment_tool = ArcNanoPaymentTool()
146
+
147
+ researcher = Agent(
148
+ role="Autonomous Intelligence Scout",
149
+ goal="Gather paid API data on Arc without leaking signer identity",
150
+ backstory="Specialized in zero-knowledge agent transactions",
151
+ tools=[payment_tool.run],
152
+ verbose=True,
153
+ )
154
+
155
+ task = Task(
156
+ description="Fetch data from https://api.inference.ai/v1/generate using shielded payment.",
157
+ expected_output="JSON data returned from the paid inference endpoint.",
158
+ agent=researcher,
159
+ )
160
+
161
+ crew = Crew(agents=[researcher], tasks=[task])
162
+ result = crew.kickoff()
163
+ print(result)
164
+ ```
165
+
166
+ ---
167
+
168
+ ## 📚 Core Modules Reference
169
+
170
+ ### `ArcAgentClient`
171
+ Autonomous HTTP client with session pooling and automated payment retry:
172
+ - `client.request(method, url, headers=None, auto_pay=True, **kwargs)`
173
+ - `client.get(url, **kwargs)`
174
+ - `client.post(url, **kwargs)`
175
+ - `client.parse_invoice(response)`: Extracts `recipient`, `pool`, `root`, and `denomination` from RFC `WWW-Authenticate` headers or JSON body.
176
+
177
+ ### `NoteVault`
178
+ Encrypted local note manager storing client-side shielded notes:
179
+ - `vault.create_note(denomination=10000)`: Generates cryptographically secure random `secret` and `nullifier_seed` scalars in the BN254 field and computes Poseidon commitments.
180
+ - `vault.get_unspent_note(denomination=None)`: Retrieves the first unspent note with sufficient value.
181
+ - `vault.mark_spent(nullifier_hash)`: Marks note as consumed to prevent client-side replays.
182
+ - `vault.unspent_balance()`: Total available unspent units.
183
+ - `vault.save(filepath)` / `vault.load(filepath)`: JSON file persistence.
184
+
185
+ ### `Groth16Prover`
186
+ RAM-based Groth16 spend proof generator:
187
+ - Binds public signals `[root, nullifierHash, recipient, aspRoot]` to the provider's wallet.
188
+ - Prevents frontrunning: proof is cryptographically tied to the exact recipient address specified in the invoice.
189
+
190
+ ---
191
+
192
+ ## 🌐 Network & Contract Reference
193
+
194
+ | Parameter | Value |
195
+ | :--- | :--- |
196
+ | **Network** | Arc Testnet (Circle L1) |
197
+ | **Chain ID** | `5042002` |
198
+ | **Gas & Settlement Asset** | USDC (Native Gas & Denomination) |
199
+ | **RPC Endpoint** | `https://rpc.testnet.arc.network` |
200
+ | **Explorer** | [https://testnet.arcscan.io](https://testnet.arcscan.io) |
201
+ | **ArcNanoPool Contract** | [`0xa40d68FDEa3B6fb01c966A9d29A6fc341AE476Ca`](https://testnet.arcscan.io/address/0xa40d68FDEa3B6fb01c966A9d29A6fc341AE476Ca) |
202
+ | **Standard Denominations** | `1000` (0.001 USDC), `10000` (0.01 USDC), `100000` (0.1 USDC) |
203
+
204
+ ---
205
+
206
+ ## 🧪 Testing
207
+
208
+ ```bash
209
+ # Run test suite
210
+ pytest tests/ -v
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 📄 License
216
+
217
+ MIT © [Trymbak Mahant](https://github.com/Trymbakmahant)
@@ -0,0 +1,26 @@
1
+ """
2
+ ArcNano: Shielded Zero-Knowledge Nanopayments on Arc
3
+ Autonomous AI Agent SDK (LangChain, AutoGPT, CrewAI)
4
+ """
5
+
6
+ from .types import ShieldedNote, X402Invoice, SpendProofPayload
7
+ from .crypto import poseidon, derive_commitment, derive_nullifier_hash, BN254_FIELD_PRIME
8
+ from .vault import NoteVault
9
+ from .prover import Groth16Prover
10
+ from .client import ArcAgentClient
11
+ from .integrations import ArcNanoPaymentTool
12
+
13
+ __version__ = "0.1.0"
14
+ __all__ = [
15
+ "ShieldedNote",
16
+ "X402Invoice",
17
+ "SpendProofPayload",
18
+ "poseidon",
19
+ "derive_commitment",
20
+ "derive_nullifier_hash",
21
+ "BN254_FIELD_PRIME",
22
+ "NoteVault",
23
+ "Groth16Prover",
24
+ "ArcAgentClient",
25
+ "ArcNanoPaymentTool",
26
+ ]
@@ -0,0 +1,162 @@
1
+ import json
2
+ import logging
3
+ import re
4
+ from typing import Optional, Dict, Any, Union
5
+ import requests
6
+
7
+ from .types import ShieldedNote, X402Invoice, SpendProofPayload
8
+ from .vault import NoteVault
9
+ from .prover import Groth16Prover
10
+
11
+ logger = logging.getLogger("arczk.client")
12
+
13
+
14
+ class ArcAgentClient:
15
+ """
16
+ Autonomous AI Agent HTTP client with automatic X402 payment handling.
17
+ Intercepts HTTP 402 Payment Required, generates zero-knowledge spend proofs in memory,
18
+ and retries transparently with standardized X-Payment and Authorization headers.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ vault: Optional[NoteVault] = None,
24
+ prover: Optional[Groth16Prover] = None,
25
+ max_payment_per_call: int = 50000, # 0.05 USDC max limit per call
26
+ session: Optional[requests.Session] = None,
27
+ ):
28
+ self.vault = vault or NoteVault()
29
+ self.prover = prover or Groth16Prover()
30
+ self.max_payment_per_call = max_payment_per_call
31
+ self.session = session or requests.Session()
32
+
33
+ def parse_invoice(self, response: requests.Response) -> X402Invoice:
34
+ """Parses X402 challenge from response headers, WWW-Authenticate, or JSON body."""
35
+ headers = response.headers
36
+ recipient = headers.get("x402-recipient")
37
+ pool = headers.get("x402-pool", "0xa40d68FDEa3B6fb01c966A9d29A6fc341AE476Ca")
38
+ root = headers.get("x402-root")
39
+ asp_root = headers.get("x402-asp-root")
40
+ denomination = int(headers.get("x402-denomination", "10000"))
41
+ chain_id = int(headers.get("x402-chain-id", "5042002"))
42
+
43
+ # Parse standard RFC-style WWW-Authenticate: X402 ... header
44
+ auth_header = headers.get("www-authenticate", "")
45
+ if "X402" in auth_header:
46
+ pool_match = re.search(r'pool=["\']([^"\']+)["\']', auth_header)
47
+ if pool_match and not recipient:
48
+ pool = pool_match.group(1)
49
+ denom_match = re.search(r'denomination=["\']?(\d+)["\']?', auth_header)
50
+ if denom_match:
51
+ try:
52
+ denomination = int(denom_match.group(1))
53
+ except ValueError:
54
+ pass
55
+
56
+ # Parse response JSON body if present
57
+ raw_challenge = {}
58
+ try:
59
+ body = response.json()
60
+ raw_challenge = body
61
+ invoice_data = body.get("invoice") or body
62
+ if isinstance(invoice_data, dict):
63
+ recipient = recipient or invoice_data.get("recipient")
64
+ pool = pool or invoice_data.get("pool")
65
+ root = root or invoice_data.get("root")
66
+ asp_root = (
67
+ asp_root
68
+ or invoice_data.get("aspRoot")
69
+ or invoice_data.get("asp_root")
70
+ )
71
+ if "denomination" in invoice_data:
72
+ try:
73
+ denomination = int(invoice_data["denomination"])
74
+ except (ValueError, TypeError):
75
+ pass
76
+ if "chainId" in invoice_data or "chain_id" in invoice_data:
77
+ try:
78
+ chain_id = int(invoice_data.get("chainId") or invoice_data.get("chain_id"))
79
+ except (ValueError, TypeError):
80
+ pass
81
+ except Exception:
82
+ pass
83
+
84
+ if not recipient or not root:
85
+ raise ValueError(f"Incomplete X402 payment invoice from server: {headers}")
86
+
87
+ return X402Invoice(
88
+ pool=pool,
89
+ denomination=denomination,
90
+ recipient=recipient,
91
+ root=root,
92
+ asp_root=asp_root or "0",
93
+ chain_id=chain_id,
94
+ raw_challenge=raw_challenge,
95
+ )
96
+
97
+ def request(
98
+ self,
99
+ method: str,
100
+ url: str,
101
+ headers: Optional[Dict[str, str]] = None,
102
+ auto_pay: bool = True,
103
+ **kwargs,
104
+ ) -> requests.Response:
105
+ """
106
+ Executes HTTP request with autonomous X402 negotiation.
107
+ """
108
+ req_headers = dict(headers or {})
109
+
110
+ # 1. Dispatch initial request
111
+ resp = self.session.request(method, url, headers=req_headers, **kwargs)
112
+
113
+ # 2. If 402 Payment Required and auto_pay enabled, negotiate ZK proof
114
+ if resp.status_code == 402 and auto_pay:
115
+ logger.info("Received HTTP 402 Payment Required from %s. Negotiating proof...", url)
116
+ invoice = self.parse_invoice(resp)
117
+
118
+ if invoice.denomination > self.max_payment_per_call:
119
+ raise PermissionError(
120
+ f"Invoice denomination ({invoice.denomination}) exceeds agent spending limit ({self.max_payment_per_call})"
121
+ )
122
+
123
+ # Retrieve unspent note from vault
124
+ note = self.vault.get_unspent_note(invoice.denomination)
125
+ if not note:
126
+ # If no note exists, create one for demonstration/testnet flow
127
+ note = self.vault.create_note(denomination=invoice.denomination)
128
+ logger.info("Generated note %s for invoice", note.nullifier_hash[:16])
129
+
130
+ # Generate Groth16 spend proof in RAM
131
+ proof_payload = self.prover.generate_proof(note, invoice)
132
+
133
+ # Attach standard X402 payment headers and retry
134
+ payload_json = json.dumps(proof_payload.to_dict())
135
+ req_headers["X-Payment"] = payload_json
136
+ req_headers["X-PAYMENT"] = payload_json
137
+ req_headers["Authorization"] = f"X402 {payload_json}"
138
+
139
+ retry_resp = self.session.request(method, url, headers=req_headers, **kwargs)
140
+
141
+ if retry_resp.status_code in (200, 201, 204):
142
+ # Mark note as spent upon successful fulfillment
143
+ self.vault.mark_spent(note.nullifier_hash)
144
+ logger.info("Payment verified and fulfilled. Note %s marked spent.", note.nullifier_hash[:16])
145
+ return retry_resp
146
+ else:
147
+ logger.warning("Payment retry failed with status %d: %s", retry_resp.status_code, retry_resp.text)
148
+ return retry_resp
149
+
150
+ return resp
151
+
152
+ def get(self, url: str, **kwargs) -> requests.Response:
153
+ return self.request("GET", url, **kwargs)
154
+
155
+ def post(self, url: str, **kwargs) -> requests.Response:
156
+ return self.request("POST", url, **kwargs)
157
+
158
+ def put(self, url: str, **kwargs) -> requests.Response:
159
+ return self.request("PUT", url, **kwargs)
160
+
161
+ def delete(self, url: str, **kwargs) -> requests.Response:
162
+ return self.request("DELETE", url, **kwargs)