defistream 1.0.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,51 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ .Python
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+
24
+ # Virtual environments
25
+ venv/
26
+ ENV/
27
+ env/
28
+ .venv/
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
+ # Type checking
44
+ .mypy_cache/
45
+
46
+ # Jupyter
47
+ .ipynb_checkpoints/
48
+
49
+ # Environment
50
+ .env
51
+ .env.local
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeFiStream
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,490 @@
1
+ Metadata-Version: 2.4
2
+ Name: defistream
3
+ Version: 1.0.0
4
+ Summary: Python client for the DeFiStream API
5
+ Project-URL: Homepage, https://defistream.dev
6
+ Project-URL: Documentation, https://docs.defistream.dev
7
+ Project-URL: Repository, https://github.com/Eren-Nevin/DeFiStream_PythonClient
8
+ Author-email: DeFiStream <support@defistream.dev>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,blockchain,crypto,defi,ethereum,web3
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
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
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: httpx>=0.25.0
24
+ Requires-Dist: pandas>=2.0.0
25
+ Requires-Dist: pyarrow>=14.0.0
26
+ Requires-Dist: pydantic>=2.0.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
30
+ Requires-Dist: python-dotenv>=1.0.0; extra == 'dev'
31
+ Provides-Extra: polars
32
+ Requires-Dist: polars>=0.20.0; extra == 'polars'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # DeFiStream Python Client
36
+
37
+ Official Python client for the [DeFiStream API](https://defistream.dev) - access historical DeFi events from 45+ EVM networks.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install defistream
43
+ ```
44
+
45
+ This includes pandas and pyarrow by default for DataFrame support.
46
+
47
+ With polars support (in addition to pandas):
48
+ ```bash
49
+ pip install defistream[polars]
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ```python
55
+ from defistream import DeFiStream
56
+
57
+ # Initialize client (reads DEFISTREAM_API_KEY from environment if not provided)
58
+ client = DeFiStream()
59
+
60
+ # Or with explicit API key
61
+ client = DeFiStream(api_key="dsk_your_api_key")
62
+
63
+ # Query ERC20 transfers using builder pattern
64
+ transfers = (
65
+ client.erc20.transfers("USDT")
66
+ .network("ETH")
67
+ .start_block(24000000)
68
+ .end_block(24100000)
69
+ .as_dict()
70
+ )
71
+
72
+ for transfer in transfers:
73
+ print(f"{transfer['sender']} -> {transfer['receiver']}: {transfer['amount']}")
74
+ ```
75
+
76
+ ## Features
77
+
78
+ - **Builder pattern**: Fluent query API with chainable methods
79
+ - **Type-safe**: Full type hints and Pydantic models
80
+ - **Multiple formats**: JSON, CSV, Parquet, pandas DataFrame, polars DataFrame
81
+ - **Async support**: Native async/await with `AsyncDeFiStream`
82
+ - **All protocols**: AAVE, Uniswap, Lido, ERC20, Native tokens, and more
83
+ - **Verbose mode**: Include all metadata fields (tx_hash, tx_id, log_index, network, name)
84
+
85
+ ## Supported Protocols
86
+
87
+ | Protocol | Events |
88
+ |----------|--------|
89
+ | ERC20 | `transfers`, `approvals` |
90
+ | Native Token | `transfers` |
91
+ | AAVE V3 | `deposits`, `withdrawals`, `borrows`, `repays`, `liquidations` |
92
+ | Uniswap V3 | `swaps`, `mints`, `burns` |
93
+ | Lido | `deposits`, `withdrawals` |
94
+ | Stader | `deposits`, `withdrawals` |
95
+ | Threshold | `mints`, `burns` |
96
+
97
+ ## Usage Examples
98
+
99
+ ### Builder Pattern
100
+
101
+ The client uses a fluent builder pattern. The query is only executed when you call a terminal method like `as_dict()`, `as_df()`, or `as_file()`.
102
+
103
+ ```python
104
+ from defistream import DeFiStream
105
+
106
+ client = DeFiStream()
107
+
108
+ # Build query step by step
109
+ query = client.erc20.transfers("USDT")
110
+ query = query.network("ETH")
111
+ query = query.start_block(24000000)
112
+ query = query.end_block(24100000)
113
+ query = query.min_amount(1000)
114
+
115
+ # Execute and get results
116
+ transfers = query.as_dict()
117
+
118
+ # Or chain everything
119
+ transfers = (
120
+ client.erc20.transfers("USDT")
121
+ .network("ETH")
122
+ .start_block(24000000)
123
+ .end_block(24100000)
124
+ .min_amount(1000)
125
+ .as_dict()
126
+ )
127
+ ```
128
+
129
+ ### ERC20 Transfers
130
+
131
+ ```python
132
+ # Get USDT transfers over 10,000 USDT
133
+ transfers = (
134
+ client.erc20.transfers("USDT")
135
+ .network("ETH")
136
+ .start_block(24000000)
137
+ .end_block(24100000)
138
+ .min_amount(10000)
139
+ .as_dict()
140
+ )
141
+
142
+ # Filter by sender
143
+ transfers = (
144
+ client.erc20.transfers("USDT")
145
+ .network("ETH")
146
+ .start_block(24000000)
147
+ .end_block(24100000)
148
+ .sender("0x28c6c06298d514db089934071355e5743bf21d60")
149
+ .as_dict()
150
+ )
151
+
152
+ # Or use aliases: from_address/to_address
153
+ transfers = (
154
+ client.erc20.transfers()
155
+ .network("ETH")
156
+ .start_block(24000000)
157
+ .end_block(24100000)
158
+ .token("USDC")
159
+ .from_address("0x...")
160
+ .to_address("0x...")
161
+ .as_dict()
162
+ )
163
+ ```
164
+
165
+ ### AAVE Events
166
+
167
+ ```python
168
+ # Get deposits
169
+ deposits = (
170
+ client.aave.deposits()
171
+ .network("ETH")
172
+ .start_block(24000000)
173
+ .end_block(24100000)
174
+ .as_dict()
175
+ )
176
+
177
+ # Get liquidations for a specific user
178
+ liquidations = (
179
+ client.aave.liquidations()
180
+ .network("ETH")
181
+ .start_block(24000000)
182
+ .end_block(24100000)
183
+ .user("0x...")
184
+ .as_dict()
185
+ )
186
+ ```
187
+
188
+ ### Uniswap Swaps
189
+
190
+ ```python
191
+ # Get swaps for WETH/USDC pool with 0.05% fee tier
192
+ swaps = (
193
+ client.uniswap.swaps("WETH", "USDC", 500)
194
+ .network("ETH")
195
+ .start_block(24000000)
196
+ .end_block(24100000)
197
+ .as_dict()
198
+ )
199
+
200
+ # Or build with chain methods
201
+ swaps = (
202
+ client.uniswap.swaps()
203
+ .symbol0("WETH")
204
+ .symbol1("USDC")
205
+ .fee(500)
206
+ .network("ETH")
207
+ .start_block(24000000)
208
+ .end_block(24100000)
209
+ .as_dict()
210
+ )
211
+ ```
212
+
213
+ ### Native Token Transfers
214
+
215
+ ```python
216
+ # Get ETH transfers >= 1 ETH
217
+ transfers = (
218
+ client.native_token.transfers()
219
+ .network("ETH")
220
+ .start_block(24000000)
221
+ .end_block(24100000)
222
+ .min_amount(1.0)
223
+ .as_dict()
224
+ )
225
+ ```
226
+
227
+ ### Verbose Mode
228
+
229
+ By default, responses omit metadata fields to reduce payload size. Use `.verbose()` to include all fields:
230
+
231
+ ```python
232
+ # Default: compact response (no tx_hash, tx_id, log_index, network, name)
233
+ transfers = (
234
+ client.erc20.transfers("USDT")
235
+ .network("ETH")
236
+ .start_block(24000000)
237
+ .end_block(24100000)
238
+ .as_dict()
239
+ )
240
+ # Returns: [{"block_number": 24000050, "sender": "0x...", "receiver": "0x...", "amount": 1000.0, ...}]
241
+
242
+ # Verbose: includes all metadata fields
243
+ transfers = (
244
+ client.erc20.transfers("USDT")
245
+ .network("ETH")
246
+ .start_block(24000000)
247
+ .end_block(24100000)
248
+ .verbose()
249
+ .as_dict()
250
+ )
251
+ # Returns: [{"name": "TransferEvent", "network": "ETH", "tx_id": "0x...", "tx_hash": "0x...", "log_index": 5, "block_number": 24000050, ...}]
252
+ ```
253
+
254
+ ### Return as DataFrame
255
+
256
+ ```python
257
+ # As pandas DataFrame (default)
258
+ df = (
259
+ client.erc20.transfers("USDT")
260
+ .network("ETH")
261
+ .start_block(24000000)
262
+ .end_block(24100000)
263
+ .as_df()
264
+ )
265
+
266
+ # As polars DataFrame
267
+ df = (
268
+ client.erc20.transfers("USDT")
269
+ .network("ETH")
270
+ .start_block(24000000)
271
+ .end_block(24100000)
272
+ .as_df("polars")
273
+ )
274
+ ```
275
+
276
+ ### Save to File
277
+
278
+ Format is automatically determined by file extension:
279
+
280
+ ```python
281
+ # Save as CSV
282
+ (
283
+ client.erc20.transfers("USDT")
284
+ .network("ETH")
285
+ .start_block(24000000)
286
+ .end_block(24100000)
287
+ .as_file("transfers.csv")
288
+ )
289
+
290
+ # Save as Parquet
291
+ (
292
+ client.erc20.transfers("USDT")
293
+ .network("ETH")
294
+ .start_block(24000000)
295
+ .end_block(24100000)
296
+ .as_file("transfers.parquet")
297
+ )
298
+
299
+ # Save as JSON
300
+ (
301
+ client.erc20.transfers("USDT")
302
+ .network("ETH")
303
+ .start_block(24000000)
304
+ .end_block(24100000)
305
+ .as_file("transfers.json")
306
+ )
307
+
308
+ # Explicit format (when path has no extension)
309
+ (
310
+ client.erc20.transfers("USDT")
311
+ .network("ETH")
312
+ .start_block(24000000)
313
+ .end_block(24100000)
314
+ .as_file("transfers", format="csv")
315
+ )
316
+ ```
317
+
318
+ ### Async Usage
319
+
320
+ ```python
321
+ import asyncio
322
+ from defistream import AsyncDeFiStream
323
+
324
+ async def main():
325
+ async with AsyncDeFiStream() as client:
326
+ transfers = await (
327
+ client.erc20.transfers("USDT")
328
+ .network("ETH")
329
+ .start_block(24000000)
330
+ .end_block(24100000)
331
+ .as_dict()
332
+ )
333
+ print(f"Found {len(transfers)} transfers")
334
+
335
+ asyncio.run(main())
336
+ ```
337
+
338
+ ### Multiple Networks in Parallel
339
+
340
+ ```python
341
+ import asyncio
342
+ from defistream import AsyncDeFiStream
343
+
344
+ async def fetch_all_networks():
345
+ async with AsyncDeFiStream() as client:
346
+ networks = ["ETH", "ARB", "BASE", "OP"]
347
+ tasks = [
348
+ client.erc20.transfers("USDC")
349
+ .network(net)
350
+ .start_block(24000000)
351
+ .end_block(24100000)
352
+ .as_dict()
353
+ for net in networks
354
+ ]
355
+ results = await asyncio.gather(*tasks)
356
+ return dict(zip(networks, results))
357
+
358
+ all_transfers = asyncio.run(fetch_all_networks())
359
+ ```
360
+
361
+ ## Configuration
362
+
363
+ ### Environment Variables
364
+
365
+ ```bash
366
+ export DEFISTREAM_API_KEY=dsk_your_api_key
367
+ export DEFISTREAM_BASE_URL=https://api.defistream.dev/v1 # optional
368
+ ```
369
+
370
+ ```python
371
+ from defistream import DeFiStream
372
+
373
+ # API key from environment
374
+ client = DeFiStream()
375
+
376
+ # Or explicit
377
+ client = DeFiStream(api_key="dsk_...", base_url="https://api.defistream.dev/v1")
378
+ ```
379
+
380
+ ### Timeout and Retries
381
+
382
+ ```python
383
+ client = DeFiStream(
384
+ api_key="dsk_...",
385
+ timeout=60.0, # seconds
386
+ max_retries=3
387
+ )
388
+ ```
389
+
390
+ ## Error Handling
391
+
392
+ ```python
393
+ from defistream import DeFiStream
394
+ from defistream.exceptions import (
395
+ DeFiStreamError,
396
+ AuthenticationError,
397
+ QuotaExceededError,
398
+ RateLimitError,
399
+ ValidationError
400
+ )
401
+
402
+ client = DeFiStream()
403
+
404
+ try:
405
+ transfers = (
406
+ client.erc20.transfers("USDT")
407
+ .network("ETH")
408
+ .start_block(24000000)
409
+ .end_block(24100000)
410
+ .as_dict()
411
+ )
412
+ except AuthenticationError:
413
+ print("Invalid API key")
414
+ except QuotaExceededError as e:
415
+ print(f"Quota exceeded. Remaining: {e.remaining}")
416
+ except RateLimitError as e:
417
+ print(f"Rate limited. Retry after: {e.retry_after}s")
418
+ except ValidationError as e:
419
+ print(f"Invalid request: {e.message}")
420
+ except DeFiStreamError as e:
421
+ print(f"API error: {e}")
422
+ ```
423
+
424
+ ## Response Headers
425
+
426
+ Access rate limit and quota information:
427
+
428
+ ```python
429
+ transfers = (
430
+ client.erc20.transfers("USDT")
431
+ .network("ETH")
432
+ .start_block(24000000)
433
+ .end_block(24100000)
434
+ .as_dict()
435
+ )
436
+
437
+ # Access response metadata
438
+ print(f"Rate limit: {client.last_response.rate_limit}")
439
+ print(f"Remaining quota: {client.last_response.quota_remaining}")
440
+ print(f"Request cost: {client.last_response.request_cost}")
441
+ ```
442
+
443
+ ## Builder Methods Reference
444
+
445
+ ### Common Methods (all protocols)
446
+
447
+ | Method | Description |
448
+ |--------|-------------|
449
+ | `.network(net)` | Set network (ETH, ARB, BASE, OP, etc.) |
450
+ | `.start_block(n)` | Set starting block number |
451
+ | `.end_block(n)` | Set ending block number |
452
+ | `.block_range(start, end)` | Set both start and end blocks |
453
+ | `.start_time(ts)` | Set starting time (ISO format or Unix timestamp) |
454
+ | `.end_time(ts)` | Set ending time (ISO format or Unix timestamp) |
455
+ | `.time_range(start, end)` | Set both start and end times |
456
+ | `.verbose()` | Include all metadata fields |
457
+
458
+ ### Filter Methods
459
+
460
+ | Method | Protocols | Description |
461
+ |--------|-----------|-------------|
462
+ | `.sender(addr)` | ERC20, Native | Filter by sender address |
463
+ | `.receiver(addr)` | ERC20, Native | Filter by receiver address |
464
+ | `.from_address(addr)` | ERC20, Native | Alias for sender |
465
+ | `.to_address(addr)` | ERC20, Native | Alias for receiver |
466
+ | `.min_amount(amt)` | ERC20, Native | Minimum transfer amount |
467
+ | `.token(symbol)` | ERC20 | Token symbol (USDT, USDC, etc.) |
468
+ | `.owner(addr)` | ERC20 Approvals, Lido | Filter by owner |
469
+ | `.spender(addr)` | ERC20 Approvals | Filter by spender |
470
+ | `.user(addr)` | AAVE | Filter by user |
471
+ | `.reserve(addr)` | AAVE | Filter by reserve |
472
+ | `.liquidator(addr)` | AAVE Liquidations | Filter by liquidator |
473
+ | `.symbol0(sym)` | Uniswap | First token symbol |
474
+ | `.symbol1(sym)` | Uniswap | Second token symbol |
475
+ | `.fee(tier)` | Uniswap | Fee tier (100, 500, 3000, 10000) |
476
+ | `.pool(addr)` | Uniswap | Pool address |
477
+
478
+ ### Terminal Methods
479
+
480
+ | Method | Description |
481
+ |--------|-------------|
482
+ | `.as_dict()` | Execute and return list of dicts (JSON) |
483
+ | `.as_df()` | Execute and return pandas DataFrame |
484
+ | `.as_df("polars")` | Execute and return polars DataFrame |
485
+ | `.as_file(path)` | Execute and save to file (format from extension) |
486
+ | `.as_file(path, format="csv")` | Execute and save with explicit format |
487
+
488
+ ## License
489
+
490
+ MIT License