proofgate 0.1.0__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.
proofgate/__init__.py ADDED
@@ -0,0 +1,51 @@
1
+ """
2
+ ProofGate SDK
3
+
4
+ Blockchain guardrails for AI agents. Validate transactions
5
+ before execution to prevent wallet drains, infinite approvals,
6
+ and other security risks.
7
+
8
+ Example:
9
+ >>> from proofgate import ProofGate
10
+ >>>
11
+ >>> pg = ProofGate(api_key="pg_your_key")
12
+ >>>
13
+ >>> result = await pg.validate(
14
+ ... from_address="0xAgent...",
15
+ ... to="0xContract...",
16
+ ... data="0xa9059cbb...",
17
+ ... )
18
+ >>>
19
+ >>> if result.safe:
20
+ ... # Execute transaction
21
+ ... pass
22
+ ... else:
23
+ ... print(f"Blocked: {result.reason}")
24
+ """
25
+
26
+ from proofgate.client import ProofGate, AsyncProofGate
27
+ from proofgate.types import (
28
+ ProofGateConfig,
29
+ ValidateRequest,
30
+ ValidateResponse,
31
+ ValidationCheck,
32
+ AgentCheckResponse,
33
+ EvidenceResponse,
34
+ UsageResponse,
35
+ )
36
+ from proofgate.exceptions import ProofGateError
37
+
38
+ __all__ = [
39
+ "ProofGate",
40
+ "AsyncProofGate",
41
+ "ProofGateConfig",
42
+ "ValidateRequest",
43
+ "ValidateResponse",
44
+ "ValidationCheck",
45
+ "AgentCheckResponse",
46
+ "EvidenceResponse",
47
+ "UsageResponse",
48
+ "ProofGateError",
49
+ ]
50
+
51
+ __version__ = "0.1.0"
proofgate/client.py ADDED
@@ -0,0 +1,524 @@
1
+ """ProofGate SDK Client implementations."""
2
+
3
+ from typing import Optional, Dict, Any
4
+ import httpx
5
+
6
+ from proofgate.types import (
7
+ ProofGateConfig,
8
+ ValidateRequest,
9
+ ValidateResponse,
10
+ AgentCheckResponse,
11
+ EvidenceResponse,
12
+ UsageResponse,
13
+ )
14
+ from proofgate.exceptions import ProofGateError
15
+
16
+
17
+ class AsyncProofGate:
18
+ """Async ProofGate SDK Client.
19
+
20
+ Example:
21
+ >>> from proofgate import AsyncProofGate
22
+ >>>
23
+ >>> pg = AsyncProofGate(api_key="pg_your_key")
24
+ >>>
25
+ >>> result = await pg.validate(
26
+ ... from_address="0xAgent...",
27
+ ... to="0xContract...",
28
+ ... data="0xa9059cbb...",
29
+ ... )
30
+ >>>
31
+ >>> if result.safe:
32
+ ... # Execute transaction
33
+ ... pass
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ api_key: str,
39
+ *,
40
+ base_url: str = "https://www.proofgate.xyz/api",
41
+ chain_id: int = 56,
42
+ guardrail_id: Optional[str] = None,
43
+ timeout: float = 30.0,
44
+ ):
45
+ """Initialize ProofGate client.
46
+
47
+ Args:
48
+ api_key: API key from ProofGate dashboard (starts with pg_)
49
+ base_url: Base URL for API (default: https://www.proofgate.xyz/api)
50
+ chain_id: Default chain ID (default: 56 for BSC)
51
+ guardrail_id: Default guardrail ID to use for validations
52
+ timeout: Request timeout in seconds (default: 30.0)
53
+ """
54
+ if not api_key:
55
+ raise ProofGateError(
56
+ "API key is required. Get one at https://www.proofgate.xyz/dashboard",
57
+ "MISSING_API_KEY",
58
+ )
59
+
60
+ if not api_key.startswith("pg_"):
61
+ raise ProofGateError(
62
+ 'Invalid API key format. Keys start with "pg_"',
63
+ "INVALID_API_KEY",
64
+ )
65
+
66
+ self.config = ProofGateConfig(
67
+ api_key=api_key,
68
+ base_url=base_url,
69
+ chain_id=chain_id,
70
+ guardrail_id=guardrail_id,
71
+ timeout=timeout,
72
+ )
73
+
74
+ self._client = httpx.AsyncClient(
75
+ base_url=base_url,
76
+ timeout=timeout,
77
+ headers={
78
+ "Content-Type": "application/json",
79
+ "X-API-Key": api_key,
80
+ },
81
+ )
82
+
83
+ async def __aenter__(self) -> "AsyncProofGate":
84
+ return self
85
+
86
+ async def __aexit__(self, *args: Any) -> None:
87
+ await self.close()
88
+
89
+ async def close(self) -> None:
90
+ """Close the HTTP client."""
91
+ await self._client.aclose()
92
+
93
+ async def _request(
94
+ self,
95
+ method: str,
96
+ path: str,
97
+ json: Optional[Dict[str, Any]] = None,
98
+ ) -> Dict[str, Any]:
99
+ """Make an API request."""
100
+ try:
101
+ response = await self._client.request(method, path, json=json)
102
+ data = response.json()
103
+
104
+ if not response.is_success:
105
+ raise ProofGateError(
106
+ data.get("error") or data.get("message") or f"HTTP {response.status_code}",
107
+ "API_ERROR",
108
+ response.status_code,
109
+ )
110
+
111
+ return data
112
+
113
+ except httpx.TimeoutException:
114
+ raise ProofGateError("Request timeout", "TIMEOUT")
115
+ except httpx.RequestError as e:
116
+ raise ProofGateError(str(e), "NETWORK_ERROR")
117
+
118
+ async def validate(
119
+ self,
120
+ from_address: str,
121
+ to: str,
122
+ data: str,
123
+ value: str = "0",
124
+ guardrail_id: Optional[str] = None,
125
+ chain_id: Optional[int] = None,
126
+ ) -> ValidateResponse:
127
+ """Validate a transaction before execution.
128
+
129
+ Args:
130
+ from_address: Sender address (your agent's wallet)
131
+ to: Target contract address
132
+ data: Transaction calldata
133
+ value: Value in wei (default: "0")
134
+ guardrail_id: Guardrail ID (overrides default)
135
+ chain_id: Chain ID (overrides default)
136
+
137
+ Returns:
138
+ Validation result
139
+
140
+ Example:
141
+ >>> result = await pg.validate(
142
+ ... from_address="0xAgent...",
143
+ ... to="0xUniswap...",
144
+ ... data="0x38ed1739...", # swap calldata
145
+ ... )
146
+ >>>
147
+ >>> if result.safe:
148
+ ... # Execute the swap
149
+ ... pass
150
+ >>> else:
151
+ ... print(f"Blocked: {result.reason}")
152
+ """
153
+ response = await self._request(
154
+ "POST",
155
+ "/validate",
156
+ json={
157
+ "from": from_address,
158
+ "to": to,
159
+ "data": data,
160
+ "value": value,
161
+ "guardrailId": guardrail_id or self.config.guardrail_id,
162
+ "chainId": chain_id or self.config.chain_id,
163
+ },
164
+ )
165
+ return ValidateResponse.model_validate(response)
166
+
167
+ async def validate_or_throw(
168
+ self,
169
+ from_address: str,
170
+ to: str,
171
+ data: str,
172
+ value: str = "0",
173
+ guardrail_id: Optional[str] = None,
174
+ chain_id: Optional[int] = None,
175
+ ) -> ValidateResponse:
176
+ """Validate and throw if unsafe (convenience method).
177
+
178
+ Args:
179
+ from_address: Sender address (your agent's wallet)
180
+ to: Target contract address
181
+ data: Transaction calldata
182
+ value: Value in wei (default: "0")
183
+ guardrail_id: Guardrail ID (overrides default)
184
+ chain_id: Chain ID (overrides default)
185
+
186
+ Returns:
187
+ Validation result (only if safe)
188
+
189
+ Raises:
190
+ ProofGateError: If validation fails
191
+
192
+ Example:
193
+ >>> try:
194
+ ... await pg.validate_or_throw(from_address, to, data)
195
+ ... # Safe to execute
196
+ ... await wallet.send_transaction(to=to, data=data)
197
+ ... except ProofGateError as e:
198
+ ... print(f"Blocked: {e.message}")
199
+ """
200
+ result = await self.validate(
201
+ from_address=from_address,
202
+ to=to,
203
+ data=data,
204
+ value=value,
205
+ guardrail_id=guardrail_id,
206
+ chain_id=chain_id,
207
+ )
208
+
209
+ if not result.safe:
210
+ raise ProofGateError(
211
+ result.reason,
212
+ "VALIDATION_FAILED",
213
+ validation_result=result,
214
+ )
215
+
216
+ return result
217
+
218
+ async def check_agent(self, wallet: str) -> AgentCheckResponse:
219
+ """Check an agent's trust score and verification status.
220
+
221
+ Args:
222
+ wallet: Agent wallet address
223
+
224
+ Returns:
225
+ Agent verification info
226
+
227
+ Example:
228
+ >>> agent = await pg.check_agent("0x123...")
229
+ >>>
230
+ >>> if agent.verification_status == "verified":
231
+ ... print(f"Trusted agent: {agent.tier_emoji} {agent.trust_score}/100")
232
+ >>> else:
233
+ ... print("Warning: Unverified agent")
234
+ """
235
+ response = await self._request("GET", f"/agents/check?wallet={wallet}")
236
+ return AgentCheckResponse.model_validate(response)
237
+
238
+ async def get_evidence(self, validation_id: str) -> EvidenceResponse:
239
+ """Get evidence for a past validation.
240
+
241
+ Args:
242
+ validation_id: Validation ID
243
+
244
+ Returns:
245
+ Evidence details
246
+
247
+ Example:
248
+ >>> evidence = await pg.get_evidence("val_abc123")
249
+ >>> print(evidence.transaction)
250
+ >>> print(evidence.result)
251
+ """
252
+ response = await self._request("GET", f"/evidence/{validation_id}")
253
+ return EvidenceResponse.model_validate(response)
254
+
255
+ async def get_usage(self, wallet: str) -> UsageResponse:
256
+ """Get validation usage stats for a wallet.
257
+
258
+ Args:
259
+ wallet: Wallet address
260
+
261
+ Returns:
262
+ Usage statistics
263
+ """
264
+ response = await self._request("GET", f"/validate?wallet={wallet}")
265
+ return UsageResponse.model_validate(response)
266
+
267
+
268
+ class ProofGate:
269
+ """Synchronous ProofGate SDK Client.
270
+
271
+ Uses httpx sync client under the hood. For async applications,
272
+ use AsyncProofGate instead.
273
+
274
+ Example:
275
+ >>> from proofgate import ProofGate
276
+ >>>
277
+ >>> pg = ProofGate(api_key="pg_your_key")
278
+ >>>
279
+ >>> result = pg.validate(
280
+ ... from_address="0xAgent...",
281
+ ... to="0xContract...",
282
+ ... data="0xa9059cbb...",
283
+ ... )
284
+ >>>
285
+ >>> if result.safe:
286
+ ... # Execute transaction
287
+ ... pass
288
+ """
289
+
290
+ def __init__(
291
+ self,
292
+ api_key: str,
293
+ *,
294
+ base_url: str = "https://www.proofgate.xyz/api",
295
+ chain_id: int = 56,
296
+ guardrail_id: Optional[str] = None,
297
+ timeout: float = 30.0,
298
+ ):
299
+ """Initialize ProofGate client.
300
+
301
+ Args:
302
+ api_key: API key from ProofGate dashboard (starts with pg_)
303
+ base_url: Base URL for API (default: https://www.proofgate.xyz/api)
304
+ chain_id: Default chain ID (default: 56 for BSC)
305
+ guardrail_id: Default guardrail ID to use for validations
306
+ timeout: Request timeout in seconds (default: 30.0)
307
+ """
308
+ if not api_key:
309
+ raise ProofGateError(
310
+ "API key is required. Get one at https://www.proofgate.xyz/dashboard",
311
+ "MISSING_API_KEY",
312
+ )
313
+
314
+ if not api_key.startswith("pg_"):
315
+ raise ProofGateError(
316
+ 'Invalid API key format. Keys start with "pg_"',
317
+ "INVALID_API_KEY",
318
+ )
319
+
320
+ self.config = ProofGateConfig(
321
+ api_key=api_key,
322
+ base_url=base_url,
323
+ chain_id=chain_id,
324
+ guardrail_id=guardrail_id,
325
+ timeout=timeout,
326
+ )
327
+
328
+ self._client = httpx.Client(
329
+ base_url=base_url,
330
+ timeout=timeout,
331
+ headers={
332
+ "Content-Type": "application/json",
333
+ "X-API-Key": api_key,
334
+ },
335
+ )
336
+
337
+ def __enter__(self) -> "ProofGate":
338
+ return self
339
+
340
+ def __exit__(self, *args: Any) -> None:
341
+ self.close()
342
+
343
+ def close(self) -> None:
344
+ """Close the HTTP client."""
345
+ self._client.close()
346
+
347
+ def _request(
348
+ self,
349
+ method: str,
350
+ path: str,
351
+ json: Optional[Dict[str, Any]] = None,
352
+ ) -> Dict[str, Any]:
353
+ """Make an API request."""
354
+ try:
355
+ response = self._client.request(method, path, json=json)
356
+ data = response.json()
357
+
358
+ if not response.is_success:
359
+ raise ProofGateError(
360
+ data.get("error") or data.get("message") or f"HTTP {response.status_code}",
361
+ "API_ERROR",
362
+ response.status_code,
363
+ )
364
+
365
+ return data
366
+
367
+ except httpx.TimeoutException:
368
+ raise ProofGateError("Request timeout", "TIMEOUT")
369
+ except httpx.RequestError as e:
370
+ raise ProofGateError(str(e), "NETWORK_ERROR")
371
+
372
+ def validate(
373
+ self,
374
+ from_address: str,
375
+ to: str,
376
+ data: str,
377
+ value: str = "0",
378
+ guardrail_id: Optional[str] = None,
379
+ chain_id: Optional[int] = None,
380
+ ) -> ValidateResponse:
381
+ """Validate a transaction before execution.
382
+
383
+ Args:
384
+ from_address: Sender address (your agent's wallet)
385
+ to: Target contract address
386
+ data: Transaction calldata
387
+ value: Value in wei (default: "0")
388
+ guardrail_id: Guardrail ID (overrides default)
389
+ chain_id: Chain ID (overrides default)
390
+
391
+ Returns:
392
+ Validation result
393
+ """
394
+ response = self._request(
395
+ "POST",
396
+ "/validate",
397
+ json={
398
+ "from": from_address,
399
+ "to": to,
400
+ "data": data,
401
+ "value": value,
402
+ "guardrailId": guardrail_id or self.config.guardrail_id,
403
+ "chainId": chain_id or self.config.chain_id,
404
+ },
405
+ )
406
+ return ValidateResponse.model_validate(response)
407
+
408
+ def validate_or_throw(
409
+ self,
410
+ from_address: str,
411
+ to: str,
412
+ data: str,
413
+ value: str = "0",
414
+ guardrail_id: Optional[str] = None,
415
+ chain_id: Optional[int] = None,
416
+ ) -> ValidateResponse:
417
+ """Validate and throw if unsafe (convenience method).
418
+
419
+ Args:
420
+ from_address: Sender address (your agent's wallet)
421
+ to: Target contract address
422
+ data: Transaction calldata
423
+ value: Value in wei (default: "0")
424
+ guardrail_id: Guardrail ID (overrides default)
425
+ chain_id: Chain ID (overrides default)
426
+
427
+ Returns:
428
+ Validation result (only if safe)
429
+
430
+ Raises:
431
+ ProofGateError: If validation fails
432
+ """
433
+ result = self.validate(
434
+ from_address=from_address,
435
+ to=to,
436
+ data=data,
437
+ value=value,
438
+ guardrail_id=guardrail_id,
439
+ chain_id=chain_id,
440
+ )
441
+
442
+ if not result.safe:
443
+ raise ProofGateError(
444
+ result.reason,
445
+ "VALIDATION_FAILED",
446
+ validation_result=result,
447
+ )
448
+
449
+ return result
450
+
451
+ def check_agent(self, wallet: str) -> AgentCheckResponse:
452
+ """Check an agent's trust score and verification status.
453
+
454
+ Args:
455
+ wallet: Agent wallet address
456
+
457
+ Returns:
458
+ Agent verification info
459
+ """
460
+ response = self._request("GET", f"/agents/check?wallet={wallet}")
461
+ return AgentCheckResponse.model_validate(response)
462
+
463
+ def get_evidence(self, validation_id: str) -> EvidenceResponse:
464
+ """Get evidence for a past validation.
465
+
466
+ Args:
467
+ validation_id: Validation ID
468
+
469
+ Returns:
470
+ Evidence details
471
+ """
472
+ response = self._request("GET", f"/evidence/{validation_id}")
473
+ return EvidenceResponse.model_validate(response)
474
+
475
+ def get_usage(self, wallet: str) -> UsageResponse:
476
+ """Get validation usage stats for a wallet.
477
+
478
+ Args:
479
+ wallet: Wallet address
480
+
481
+ Returns:
482
+ Usage statistics
483
+ """
484
+ response = self._request("GET", f"/validate?wallet={wallet}")
485
+ return UsageResponse.model_validate(response)
486
+
487
+
488
+ def is_transaction_safe(
489
+ api_key: str,
490
+ from_address: str,
491
+ to: str,
492
+ data: str,
493
+ value: str = "0",
494
+ ) -> bool:
495
+ """Quick validation helper.
496
+
497
+ Args:
498
+ api_key: ProofGate API key
499
+ from_address: Sender address
500
+ to: Target contract address
501
+ data: Transaction calldata
502
+ value: Value in wei (default: "0")
503
+
504
+ Returns:
505
+ Whether the transaction is safe
506
+
507
+ Example:
508
+ >>> from proofgate import is_transaction_safe
509
+ >>>
510
+ >>> safe = is_transaction_safe(
511
+ ... "pg_xxx",
512
+ ... from_address=agent,
513
+ ... to=contract,
514
+ ... data=calldata,
515
+ ... )
516
+ """
517
+ with ProofGate(api_key=api_key) as pg:
518
+ result = pg.validate(
519
+ from_address=from_address,
520
+ to=to,
521
+ data=data,
522
+ value=value,
523
+ )
524
+ return result.safe
@@ -0,0 +1,36 @@
1
+ """Exceptions for ProofGate SDK."""
2
+
3
+ from typing import Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from proofgate.types import ValidateResponse
7
+
8
+
9
+ class ProofGateError(Exception):
10
+ """Base exception for ProofGate errors.
11
+
12
+ Attributes:
13
+ message: Human-readable error message
14
+ code: Error code (e.g., 'VALIDATION_FAILED', 'API_ERROR')
15
+ status_code: HTTP status code (if applicable)
16
+ validation_result: Validation result (if validation failed)
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ message: str,
22
+ code: str,
23
+ status_code: Optional[int] = None,
24
+ validation_result: Optional["ValidateResponse"] = None,
25
+ ):
26
+ super().__init__(message)
27
+ self.message = message
28
+ self.code = code
29
+ self.status_code = status_code
30
+ self.validation_result = validation_result
31
+
32
+ def __str__(self) -> str:
33
+ return f"[{self.code}] {self.message}"
34
+
35
+ def __repr__(self) -> str:
36
+ return f"ProofGateError(code={self.code!r}, message={self.message!r})"
proofgate/types.py ADDED
@@ -0,0 +1,179 @@
1
+ """Type definitions for ProofGate SDK."""
2
+
3
+ from typing import Optional, List, Literal
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class ProofGateConfig(BaseModel):
8
+ """Configuration for ProofGate client."""
9
+
10
+ api_key: str = Field(..., description="API key from ProofGate dashboard (starts with pg_)")
11
+ base_url: str = Field(
12
+ default="https://www.proofgate.xyz/api",
13
+ description="Base URL for API"
14
+ )
15
+ chain_id: int = Field(default=56, description="Default chain ID (56 = BSC)")
16
+ guardrail_id: Optional[str] = Field(
17
+ default=None,
18
+ description="Default guardrail ID to use for validations"
19
+ )
20
+ timeout: float = Field(default=30.0, description="Request timeout in seconds")
21
+
22
+
23
+ class ValidateRequest(BaseModel):
24
+ """Request for transaction validation."""
25
+
26
+ from_address: str = Field(..., alias="from", description="Sender address (your agent's wallet)")
27
+ to: str = Field(..., description="Target contract address")
28
+ data: str = Field(..., description="Transaction calldata")
29
+ value: str = Field(default="0", description="Value in wei")
30
+ guardrail_id: Optional[str] = Field(default=None, description="Guardrail ID (overrides default)")
31
+ chain_id: Optional[int] = Field(default=None, description="Chain ID (overrides default)")
32
+
33
+ class Config:
34
+ populate_by_name = True
35
+
36
+
37
+ class ValidationCheck(BaseModel):
38
+ """Individual check result from validation."""
39
+
40
+ name: str = Field(..., description="Check name (e.g., 'allowed_contracts', 'daily_limit')")
41
+ passed: bool = Field(..., description="Did this check pass?")
42
+ details: str = Field(..., description="Human-readable details")
43
+ severity: Literal["info", "warning", "critical"] = Field(
44
+ ..., description="Severity level"
45
+ )
46
+
47
+
48
+ class ValidateResponse(BaseModel):
49
+ """Response from transaction validation."""
50
+
51
+ validation_id: str = Field(..., alias="validationId", description="Unique validation ID")
52
+ result: Literal["PASS", "FAIL", "PENDING"] = Field(..., description="Validation result")
53
+ reason: str = Field(..., description="Human-readable reason")
54
+ evidence_uri: str = Field(..., alias="evidenceUri", description="Evidence URI")
55
+ safe: bool = Field(..., description="Is the transaction safe to execute?")
56
+ checks: List[ValidationCheck] = Field(default_factory=list, description="Detailed check results")
57
+ chain_id: int = Field(..., alias="chainId", description="Chain ID validated on")
58
+ authenticated: bool = Field(default=False, description="Was API key authenticated?")
59
+ tier: str = Field(default="free", description="User tier (free/pro)")
60
+ backend: str = Field(default="local", description="Backend used (local/evidence-service)")
61
+ on_chain_recorded: bool = Field(
62
+ default=False, alias="onChainRecorded", description="Was proof recorded on-chain?"
63
+ )
64
+
65
+ class Config:
66
+ populate_by_name = True
67
+
68
+
69
+ class AgentStats(BaseModel):
70
+ """Validation statistics for an agent."""
71
+
72
+ total_validations: int = Field(..., alias="totalValidations")
73
+ passed_validations: int = Field(..., alias="passedValidations")
74
+ failed_validations: int = Field(..., alias="failedValidations")
75
+ pass_rate: float = Field(..., alias="passRate")
76
+
77
+ class Config:
78
+ populate_by_name = True
79
+
80
+
81
+ class AgentRegistration(BaseModel):
82
+ """Registration info for an agent."""
83
+
84
+ name: Optional[str] = None
85
+ registered_at: str = Field(..., alias="registeredAt")
86
+
87
+ class Config:
88
+ populate_by_name = True
89
+
90
+
91
+ class AgentCheckResponse(BaseModel):
92
+ """Response from agent check."""
93
+
94
+ wallet: str = Field(..., description="Wallet address (lowercase)")
95
+ is_registered: bool = Field(..., alias="isRegistered", description="Is this agent registered?")
96
+ verification_status: Literal["verified", "registered", "unverified", "unknown"] = Field(
97
+ ..., alias="verificationStatus", description="Verification status"
98
+ )
99
+ verification_message: str = Field(..., alias="verificationMessage", description="Human-readable message")
100
+ trust_score: int = Field(..., alias="trustScore", description="Trust score (0-100)")
101
+ tier: Literal["diamond", "gold", "silver", "bronze", "unverified"] = Field(
102
+ ..., description="Trust tier"
103
+ )
104
+ tier_emoji: str = Field(..., alias="tierEmoji", description="Tier emoji")
105
+ tier_name: str = Field(..., alias="tierName", description="Tier display name")
106
+ stats: AgentStats = Field(..., description="Validation statistics")
107
+ registration: Optional[AgentRegistration] = Field(
108
+ default=None, description="Registration info (if registered)"
109
+ )
110
+ recommendation: str = Field(..., description="Safety recommendation")
111
+
112
+ class Config:
113
+ populate_by_name = True
114
+
115
+
116
+ class EvidenceTransaction(BaseModel):
117
+ """Transaction details in evidence."""
118
+
119
+ from_address: str = Field(..., alias="from")
120
+ to: str
121
+ data: str
122
+ value: str
123
+
124
+ class Config:
125
+ populate_by_name = True
126
+
127
+
128
+ class EvidenceResult(BaseModel):
129
+ """Validation result in evidence."""
130
+
131
+ status: Literal["PASS", "FAIL", "PENDING"]
132
+ reason: str
133
+ safe: bool
134
+
135
+
136
+ class EvidenceAgent(BaseModel):
137
+ """Agent info in evidence."""
138
+
139
+ wallet: str
140
+ name: Optional[str] = None
141
+ verified: bool
142
+
143
+
144
+ class EvidenceProof(BaseModel):
145
+ """Proof metadata in evidence."""
146
+
147
+ authenticated: bool
148
+ on_chain_recorded: bool = Field(..., alias="onChainRecorded")
149
+ batch_id: Optional[str] = Field(default=None, alias="batchId")
150
+ recorded_at: Optional[str] = Field(default=None, alias="recordedAt")
151
+
152
+ class Config:
153
+ populate_by_name = True
154
+
155
+
156
+ class EvidenceResponse(BaseModel):
157
+ """Response from evidence retrieval."""
158
+
159
+ validation_id: str = Field(..., alias="validationId", description="Validation ID")
160
+ timestamp: str = Field(..., description="Timestamp")
161
+ chain_id: int = Field(..., alias="chainId", description="Chain ID")
162
+ transaction: EvidenceTransaction = Field(..., description="Transaction details")
163
+ result: EvidenceResult = Field(..., description="Validation result")
164
+ guardrail_id: Optional[str] = Field(default=None, alias="guardrailId", description="Guardrail used")
165
+ agent: EvidenceAgent = Field(..., description="Agent info")
166
+ proof: EvidenceProof = Field(..., description="Proof metadata")
167
+
168
+ class Config:
169
+ populate_by_name = True
170
+
171
+
172
+ class UsageResponse(BaseModel):
173
+ """Response from usage check."""
174
+
175
+ wallet: str
176
+ tier: str
177
+ validations_used: int
178
+ validations_limit: int
179
+ daily_spent_wei: str
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.4
2
+ Name: proofgate
3
+ Version: 0.1.0
4
+ Summary: Official ProofGate SDK — blockchain transaction validation and guardrails for AI agents
5
+ Project-URL: Homepage, https://www.proofgate.xyz
6
+ Project-URL: Documentation, https://www.proofgate.xyz/docs
7
+ Project-URL: Repository, https://github.com/ProofGate/proofgate-python
8
+ Project-URL: Issues, https://github.com/ProofGate/proofgate-python/issues
9
+ Author-email: 0xCR6 <cristian@proofgate.xyz>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agent,blockchain,bsc,defi,ethereum,guardrails,proofgate,security,validation,web3
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Security
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.8
26
+ Requires-Dist: httpx>=0.24.0
27
+ Requires-Dist: pydantic>=2.0.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: black>=23.0.0; extra == 'dev'
30
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
32
+ Requires-Dist: pytest-httpx>=0.21.0; extra == 'dev'
33
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # proofgate
38
+
39
+ > Blockchain guardrails for AI agents. Validate transactions before execution.
40
+
41
+ [![PyPI version](https://badge.fury.io/py/proofgate.svg)](https://pypi.org/project/proofgate/)
42
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
+
45
+ ## What is ProofGate?
46
+
47
+ ProofGate validates blockchain transactions before your AI agent executes them. It prevents:
48
+
49
+ - 🚫 **Wallet drains** from prompt injection attacks
50
+ - 🚫 **Infinite approvals** to malicious contracts
51
+ - 🚫 **Excessive spending** beyond daily limits
52
+ - 🚫 **High slippage** swaps that lose money
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install proofgate
58
+ ```
59
+
60
+ ## Quick Start
61
+
62
+ ### Synchronous Usage
63
+
64
+ ```python
65
+ from proofgate import ProofGate
66
+
67
+ # Initialize client
68
+ pg = ProofGate(api_key="pg_your_api_key") # Get from proofgate.xyz/dashboard
69
+
70
+ # Validate before sending
71
+ result = pg.validate(
72
+ from_address="0xYourAgentWallet",
73
+ to="0xContractAddress",
74
+ data="0xa9059cbb...", # Transaction calldata
75
+ value="0",
76
+ )
77
+
78
+ if result.safe:
79
+ # ✅ Execute the transaction
80
+ wallet.send_transaction(to=to, data=data, value=value)
81
+ else:
82
+ # 🚫 Transaction blocked
83
+ print(f"Blocked: {result.reason}")
84
+ ```
85
+
86
+ ### Async Usage
87
+
88
+ ```python
89
+ from proofgate import AsyncProofGate
90
+
91
+ async def main():
92
+ async with AsyncProofGate(api_key="pg_your_api_key") as pg:
93
+ result = await pg.validate(
94
+ from_address="0xYourAgentWallet",
95
+ to="0xContractAddress",
96
+ data="0xa9059cbb...",
97
+ )
98
+
99
+ if result.safe:
100
+ # Execute transaction
101
+ pass
102
+ ```
103
+
104
+ ## API Reference
105
+
106
+ ### `ProofGate(config)` / `AsyncProofGate(config)`
107
+
108
+ Create a new ProofGate client.
109
+
110
+ ```python
111
+ pg = ProofGate(
112
+ api_key="pg_xxx", # Required: Your API key
113
+ chain_id=56, # Optional: Default chain (56 = BSC)
114
+ guardrail_id="xxx", # Optional: Default guardrail
115
+ base_url="https://...", # Optional: Custom API URL
116
+ timeout=30.0, # Optional: Request timeout (seconds)
117
+ )
118
+ ```
119
+
120
+ ### `pg.validate(request)`
121
+
122
+ Validate a transaction.
123
+
124
+ ```python
125
+ result = pg.validate(
126
+ from_address="0xAgent...",
127
+ to="0xContract...",
128
+ data="0x...",
129
+ value="0", # Optional
130
+ guardrail_id="xxx", # Optional: Override default
131
+ chain_id=56, # Optional: Override default
132
+ )
133
+
134
+ # Returns ValidateResponse with:
135
+ # - validation_id: str
136
+ # - result: "PASS" | "FAIL" | "PENDING"
137
+ # - reason: str
138
+ # - safe: bool
139
+ # - checks: List[ValidationCheck]
140
+ # - authenticated: bool
141
+ # - evidence_uri: str
142
+ ```
143
+
144
+ ### `pg.validate_or_throw(request)`
145
+
146
+ Validate and raise exception if unsafe.
147
+
148
+ ```python
149
+ from proofgate import ProofGateError
150
+
151
+ try:
152
+ pg.validate_or_throw(
153
+ from_address=from_addr,
154
+ to=to_addr,
155
+ data=calldata,
156
+ )
157
+ # Safe to execute
158
+ except ProofGateError as e:
159
+ print(f"Blocked: {e.message}")
160
+ ```
161
+
162
+ ### `pg.check_agent(wallet)`
163
+
164
+ Check an agent's trust score.
165
+
166
+ ```python
167
+ agent = pg.check_agent("0x123...")
168
+
169
+ print(agent.trust_score) # 85
170
+ print(agent.tier) # "gold"
171
+ print(agent.verification_status) # "verified"
172
+ ```
173
+
174
+ ### `pg.get_evidence(validation_id)`
175
+
176
+ Get evidence for a past validation.
177
+
178
+ ```python
179
+ evidence = pg.get_evidence("val_abc123")
180
+ print(evidence.transaction)
181
+ print(evidence.result)
182
+ ```
183
+
184
+ ## Guardrails
185
+
186
+ Guardrails define what your agent can do. Create them at [proofgate.xyz/guardrails](https://www.proofgate.xyz/guardrails).
187
+
188
+ Example guardrail rules:
189
+ - **Whitelist contracts**: Only Uniswap, Aave, Compound
190
+ - **Max approval**: 1,000 USDC per approval
191
+ - **Max slippage**: 1% on swaps
192
+ - **Daily limit**: $10,000 total spending
193
+
194
+ ## Error Handling
195
+
196
+ ```python
197
+ from proofgate import ProofGate, ProofGateError
198
+
199
+ try:
200
+ pg.validate(from_address=from_addr, to=to_addr, data=calldata)
201
+ except ProofGateError as e:
202
+ print(f"Code: {e.code}") # "VALIDATION_FAILED"
203
+ print(f"Message: {e.message}") # "Infinite approval detected"
204
+ print(f"Result: {e.validation_result}")
205
+ ```
206
+
207
+ Error codes:
208
+ - `MISSING_API_KEY` - No API key provided
209
+ - `INVALID_API_KEY` - Key doesn't start with `pg_`
210
+ - `VALIDATION_FAILED` - Transaction failed validation
211
+ - `API_ERROR` - API returned an error
212
+ - `NETWORK_ERROR` - Network request failed
213
+ - `TIMEOUT` - Request timed out
214
+
215
+ ## Type Hints
216
+
217
+ Full type hints included:
218
+
219
+ ```python
220
+ from proofgate import (
221
+ ProofGateConfig,
222
+ ValidateRequest,
223
+ ValidateResponse,
224
+ ValidationCheck,
225
+ AgentCheckResponse,
226
+ EvidenceResponse,
227
+ )
228
+ ```
229
+
230
+ ## Get Your API Key
231
+
232
+ 1. Go to [proofgate.xyz](https://www.proofgate.xyz)
233
+ 2. Connect your wallet
234
+ 3. Register your AI agent
235
+ 4. Copy your API key (starts with `pg_`)
236
+
237
+ **Free tier:** 100 validations/month
238
+
239
+ ## Links
240
+
241
+ - **Website:** [proofgate.xyz](https://www.proofgate.xyz)
242
+ - **Documentation:** [proofgate.xyz/docs](https://www.proofgate.xyz/docs)
243
+ - **Dashboard:** [proofgate.xyz/dashboard](https://www.proofgate.xyz/dashboard)
244
+ - **GitHub:** [github.com/ProofGate/proofgate-python](https://github.com/ProofGate/proofgate-python)
245
+
246
+ ## License
247
+
248
+ MIT © [0xCR6](https://twitter.com/0xCR6)
@@ -0,0 +1,8 @@
1
+ proofgate/__init__.py,sha256=RYOe5gCXPhsPG6-HzsnmVkdWpzE6GoIHimcZ4J9sdTE,1130
2
+ proofgate/client.py,sha256=nnCqjtD12WXU5u1dzkx6VEMxnAYiZgJ-9HSFrPdl0Fs,15765
3
+ proofgate/exceptions.py,sha256=PCbAa3hHlNvG0seXRl0j1ubbi6PGLJHyIdEE8tb0bpo,1059
4
+ proofgate/types.py,sha256=RPJPHlyi1NZ31MoLntoXlUb8S_EpdVCKbg9MQQj6XnY,6588
5
+ proofgate-0.1.0.dist-info/METADATA,sha256=oe1bbnV56br6ryNBiWfqYasIi7CcYVN9oxTorQ6qi2E,6817
6
+ proofgate-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
7
+ proofgate-0.1.0.dist-info/licenses/LICENSE,sha256=Z3Jp3T7pcPkUZiJK7_WuigXNQZYY_bpasmJg1mS9NA8,1062
8
+ proofgate-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 0xCR6
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.