fere-sdk 0.3.1.dev27__tar.gz → 0.4.0.dev29__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fere-sdk
3
- Version: 0.3.1.dev27
3
+ Version: 0.4.0.dev29
4
4
  Summary: Python SDK for the FereAI Gateway API
5
5
  Author-email: Fere AI <info@fere.ai>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fere-sdk"
3
- version = "0.3.1.dev27"
3
+ version = "0.4.0.dev29"
4
4
  description = "Python SDK for the FereAI Gateway API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -22,7 +22,9 @@ from .models import (
22
22
  DepositRequest,
23
23
  HooksRequest,
24
24
  LimitOrderRequest,
25
+ SecurityCheckResponse,
25
26
  SwapRequest,
27
+ TokenToCheck,
26
28
  WithdrawRequest,
27
29
  )
28
30
 
@@ -223,6 +225,29 @@ class FereClient:
223
225
  )
224
226
  return await self._api.set_hooks(req)
225
227
 
228
+ async def check_token_security(
229
+ self,
230
+ tokens: list[TokenToCheck] | list[dict],
231
+ ) -> SecurityCheckResponse:
232
+ """Pre-check tokens for honeypot / scam / rug-pull flags.
233
+
234
+ Pass a list of TokenToCheck or plain dicts with token_address
235
+ and chain_id. If a token is flagged and the user still wants
236
+ to proceed, call ``swap(..., bypass_security_check=True)``.
237
+ """
238
+ normalized: list[TokenToCheck] = []
239
+ for t in tokens:
240
+ if isinstance(t, TokenToCheck):
241
+ normalized.append(t)
242
+ else:
243
+ normalized.append(
244
+ TokenToCheck(
245
+ token_address=t["token_address"],
246
+ chain_id=t["chain_id"],
247
+ )
248
+ )
249
+ return await self._api.check_token_security(normalized)
250
+
226
251
  # ----------------------------------------------------------
227
252
  # Earn (blocks until complete)
228
253
  # ----------------------------------------------------------
@@ -16,10 +16,14 @@ from .models import (
16
16
  DepositRequest,
17
17
  HooksRequest,
18
18
  LimitOrderRequest,
19
+ SecurityCheckResponse,
20
+ SecurityCheckSummary,
19
21
  SwapRequest,
20
22
  TaskResponse,
21
23
  TaskStatusResponse,
22
24
  TokenInfo,
25
+ TokenSecurityResult,
26
+ TokenToCheck,
23
27
  WithdrawRequest,
24
28
  )
25
29
 
@@ -305,6 +309,49 @@ class FereAPI:
305
309
  """POST /v1/hooks"""
306
310
  return await self._authed_post("/v1/hooks", request.to_dict())
307
311
 
312
+ # ----------------------------------------------------------
313
+ # Security
314
+ # ----------------------------------------------------------
315
+
316
+ async def check_token_security(
317
+ self,
318
+ tokens: list[TokenToCheck],
319
+ ) -> SecurityCheckResponse:
320
+ """POST /v1/security/check.
321
+
322
+ Pre-check whether output tokens have been flagged by Honeypot.is
323
+ (EVM) or RugCheck.xyz (Solana) before submitting a swap. If a
324
+ token is flagged and the user still wants to proceed, pass
325
+ bypass_security_check=true on the swap request.
326
+ """
327
+ data = await self._authed_post(
328
+ "/v1/security/check",
329
+ {"tokens": [t.to_dict() for t in tokens]},
330
+ )
331
+ results = [
332
+ TokenSecurityResult(
333
+ token_address=r["token_address"],
334
+ chain_id=r["chain_id"],
335
+ allowed=r["allowed"],
336
+ status=r["status"],
337
+ provider=r["provider"],
338
+ reason=r.get("reason"),
339
+ risk_details=r.get("risk_details"),
340
+ )
341
+ for r in data.get("results", [])
342
+ ]
343
+ s = data.get("summary", {})
344
+ summary = SecurityCheckSummary(
345
+ total=s.get("total", 0),
346
+ passed=s.get("passed", 0),
347
+ failed=s.get("failed", 0),
348
+ warning=s.get("warning", 0),
349
+ api_unavailable=s.get("api_unavailable", 0),
350
+ unsupported_chain=s.get("unsupported_chain", 0),
351
+ skipped=s.get("skipped", 0),
352
+ )
353
+ return SecurityCheckResponse(results=results, summary=summary)
354
+
308
355
  # ----------------------------------------------------------
309
356
  # Wallets
310
357
  # ----------------------------------------------------------
@@ -18,6 +18,10 @@ __all__ = [
18
18
  "HooksRequest",
19
19
  "DepositRequest",
20
20
  "WithdrawRequest",
21
+ "TokenToCheck",
22
+ "TokenSecurityResult",
23
+ "SecurityCheckSummary",
24
+ "SecurityCheckResponse",
21
25
  "TaskTimeoutError",
22
26
  ]
23
27
 
@@ -78,6 +82,7 @@ class SwapRequest:
78
82
  stop_loss: dict | None = None
79
83
  take_profit: dict | None = None
80
84
  cancel_conditional_orders: bool | None = None
85
+ bypass_security_check: bool | None = None
81
86
 
82
87
  def to_dict(self) -> dict:
83
88
  d: dict = {
@@ -95,9 +100,51 @@ class SwapRequest:
95
100
  d["take_profit"] = self.take_profit
96
101
  if self.cancel_conditional_orders is not None:
97
102
  d["cancel_conditional_orders"] = self.cancel_conditional_orders
103
+ if self.bypass_security_check is not None:
104
+ d["bypass_security_check"] = self.bypass_security_check
98
105
  return d
99
106
 
100
107
 
108
+ @dataclass
109
+ class TokenToCheck:
110
+ token_address: str
111
+ chain_id: int | str
112
+
113
+ def to_dict(self) -> dict:
114
+ return {
115
+ "token_address": self.token_address,
116
+ "chain_id": self.chain_id,
117
+ }
118
+
119
+
120
+ @dataclass
121
+ class TokenSecurityResult:
122
+ token_address: str
123
+ chain_id: int
124
+ allowed: bool
125
+ status: str
126
+ provider: str
127
+ reason: str | None = None
128
+ risk_details: dict | None = None
129
+
130
+
131
+ @dataclass
132
+ class SecurityCheckSummary:
133
+ total: int
134
+ passed: int
135
+ failed: int
136
+ warning: int
137
+ api_unavailable: int
138
+ unsupported_chain: int = 0
139
+ skipped: int = 0
140
+
141
+
142
+ @dataclass
143
+ class SecurityCheckResponse:
144
+ results: list[TokenSecurityResult]
145
+ summary: SecurityCheckSummary
146
+
147
+
101
148
  @dataclass
102
149
  class LimitOrderRequest:
103
150
  chain_id_in: int
File without changes