defistream 1.0.0__py3-none-any.whl → 1.0.2__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.
- defistream/__init__.py +10 -13
- defistream/protocols.py +150 -65
- defistream/query.py +43 -71
- {defistream-1.0.0.dist-info → defistream-1.0.2.dist-info}/METADATA +98 -149
- defistream-1.0.2.dist-info/RECORD +11 -0
- defistream-1.0.0.dist-info/RECORD +0 -11
- {defistream-1.0.0.dist-info → defistream-1.0.2.dist-info}/WHEEL +0 -0
- {defistream-1.0.0.dist-info → defistream-1.0.2.dist-info}/licenses/LICENSE +0 -0
defistream/__init__.py
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
"""
|
|
2
2
|
DeFiStream Python Client
|
|
3
3
|
|
|
4
|
-
Official Python client for the DeFiStream API
|
|
5
|
-
DeFi events from 45+ EVM networks.
|
|
4
|
+
Official Python client for the DeFiStream API.
|
|
6
5
|
|
|
7
6
|
Example:
|
|
8
7
|
>>> from defistream import DeFiStream
|
|
9
8
|
>>> client = DeFiStream(api_key="dsk_...")
|
|
10
9
|
>>>
|
|
11
|
-
>>> # Builder pattern
|
|
12
|
-
>>>
|
|
13
|
-
|
|
10
|
+
>>> # Builder pattern - returns pandas DataFrame
|
|
11
|
+
>>> df = (
|
|
12
|
+
... client.erc20.transfers("USDT")
|
|
13
|
+
... .network("ETH")
|
|
14
|
+
... .block_range(21000000, 21010000)
|
|
15
|
+
... .as_df()
|
|
16
|
+
... )
|
|
14
17
|
>>>
|
|
15
18
|
>>> # Save to file
|
|
16
|
-
>>> query.
|
|
19
|
+
>>> query.as_file("transfers.parquet")
|
|
17
20
|
"""
|
|
18
21
|
|
|
19
22
|
from .client import AsyncDeFiStream, DeFiStream
|
|
@@ -32,20 +35,17 @@ from .models import (
|
|
|
32
35
|
AAVELiquidationEvent,
|
|
33
36
|
AAVERepayEvent,
|
|
34
37
|
AAVEWithdrawEvent,
|
|
35
|
-
ERC20ApprovalEvent,
|
|
36
38
|
ERC20TransferEvent,
|
|
37
39
|
EventBase,
|
|
38
40
|
LidoDepositEvent,
|
|
39
41
|
LidoWithdrawEvent,
|
|
40
42
|
NativeTransferEvent,
|
|
41
43
|
ResponseMetadata,
|
|
42
|
-
UniswapBurnEvent,
|
|
43
|
-
UniswapMintEvent,
|
|
44
44
|
UniswapSwapEvent,
|
|
45
45
|
)
|
|
46
46
|
from .query import AsyncQueryBuilder, QueryBuilder
|
|
47
47
|
|
|
48
|
-
__version__ = "
|
|
48
|
+
__version__ = "1.0.2"
|
|
49
49
|
|
|
50
50
|
__all__ = [
|
|
51
51
|
# Clients
|
|
@@ -66,7 +66,6 @@ __all__ = [
|
|
|
66
66
|
"EventBase",
|
|
67
67
|
"ResponseMetadata",
|
|
68
68
|
"ERC20TransferEvent",
|
|
69
|
-
"ERC20ApprovalEvent",
|
|
70
69
|
"NativeTransferEvent",
|
|
71
70
|
"AAVEDepositEvent",
|
|
72
71
|
"AAVEWithdrawEvent",
|
|
@@ -74,8 +73,6 @@ __all__ = [
|
|
|
74
73
|
"AAVERepayEvent",
|
|
75
74
|
"AAVELiquidationEvent",
|
|
76
75
|
"UniswapSwapEvent",
|
|
77
|
-
"UniswapMintEvent",
|
|
78
|
-
"UniswapBurnEvent",
|
|
79
76
|
"LidoDepositEvent",
|
|
80
77
|
"LidoWithdrawEvent",
|
|
81
78
|
]
|
defistream/protocols.py
CHANGED
|
@@ -21,34 +21,25 @@ class ERC20Protocol:
|
|
|
21
21
|
Start a query for ERC20 transfer events.
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
|
-
token:
|
|
24
|
+
token: Token symbol (USDT, USDC, WETH, etc.) or contract address
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
27
27
|
QueryBuilder for chaining filters
|
|
28
28
|
|
|
29
29
|
Example:
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
df = (
|
|
31
|
+
client.erc20.transfers("USDT")
|
|
32
|
+
.network("ETH")
|
|
33
|
+
.block_range(21000000, 21010000)
|
|
34
|
+
.as_df()
|
|
35
|
+
)
|
|
32
36
|
"""
|
|
33
37
|
params = {"token": token} if token else {}
|
|
34
38
|
return QueryBuilder(self._client, "/erc20/events/transfer", params)
|
|
35
39
|
|
|
36
|
-
def approvals(self, token: str | None = None) -> QueryBuilder:
|
|
37
|
-
"""
|
|
38
|
-
Start a query for ERC20 approval events.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
token: Optional token symbol
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
QueryBuilder for chaining filters
|
|
45
|
-
"""
|
|
46
|
-
params = {"token": token} if token else {}
|
|
47
|
-
return QueryBuilder(self._client, "/erc20/events/approval", params)
|
|
48
|
-
|
|
49
40
|
|
|
50
41
|
class NativeTokenProtocol:
|
|
51
|
-
"""Native token (ETH, MATIC,
|
|
42
|
+
"""Native token (ETH, MATIC, etc.) events with builder pattern."""
|
|
52
43
|
|
|
53
44
|
def __init__(self, client: "BaseClient"):
|
|
54
45
|
self._client = client
|
|
@@ -61,8 +52,13 @@ class NativeTokenProtocol:
|
|
|
61
52
|
QueryBuilder for chaining filters
|
|
62
53
|
|
|
63
54
|
Example:
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
df = (
|
|
56
|
+
client.native_token.transfers()
|
|
57
|
+
.network("ETH")
|
|
58
|
+
.block_range(21000000, 21010000)
|
|
59
|
+
.min_amount(1.0)
|
|
60
|
+
.as_df()
|
|
61
|
+
)
|
|
66
62
|
"""
|
|
67
63
|
return QueryBuilder(self._client, "/native_token/events/transfer")
|
|
68
64
|
|
|
@@ -89,6 +85,10 @@ class AAVEProtocol:
|
|
|
89
85
|
"""Start a query for AAVE repay events."""
|
|
90
86
|
return QueryBuilder(self._client, "/aave/events/repay")
|
|
91
87
|
|
|
88
|
+
def flashloans(self) -> QueryBuilder:
|
|
89
|
+
"""Start a query for AAVE flash loan events."""
|
|
90
|
+
return QueryBuilder(self._client, "/aave/events/flashloan")
|
|
91
|
+
|
|
92
92
|
def liquidations(self) -> QueryBuilder:
|
|
93
93
|
"""Start a query for AAVE liquidation events."""
|
|
94
94
|
return QueryBuilder(self._client, "/aave/events/liquidation")
|
|
@@ -110,16 +110,20 @@ class UniswapProtocol:
|
|
|
110
110
|
Start a query for Uniswap V3 swap events.
|
|
111
111
|
|
|
112
112
|
Args:
|
|
113
|
-
symbol0:
|
|
114
|
-
symbol1:
|
|
115
|
-
fee:
|
|
113
|
+
symbol0: First token symbol (e.g., WETH)
|
|
114
|
+
symbol1: Second token symbol (e.g., USDC)
|
|
115
|
+
fee: Fee tier (100, 500, 3000, 10000)
|
|
116
116
|
|
|
117
117
|
Returns:
|
|
118
118
|
QueryBuilder for chaining filters
|
|
119
119
|
|
|
120
120
|
Example:
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
df = (
|
|
122
|
+
client.uniswap.swaps("WETH", "USDC", 500)
|
|
123
|
+
.network("ETH")
|
|
124
|
+
.block_range(21000000, 21010000)
|
|
125
|
+
.as_df()
|
|
126
|
+
)
|
|
123
127
|
"""
|
|
124
128
|
params: dict[str, Any] = {}
|
|
125
129
|
if symbol0:
|
|
@@ -130,13 +134,13 @@ class UniswapProtocol:
|
|
|
130
134
|
params["fee"] = fee
|
|
131
135
|
return QueryBuilder(self._client, "/uniswap/events/swap", params)
|
|
132
136
|
|
|
133
|
-
def
|
|
137
|
+
def deposits(
|
|
134
138
|
self,
|
|
135
139
|
symbol0: str | None = None,
|
|
136
140
|
symbol1: str | None = None,
|
|
137
141
|
fee: int | None = None,
|
|
138
142
|
) -> QueryBuilder:
|
|
139
|
-
"""Start a query for Uniswap V3
|
|
143
|
+
"""Start a query for Uniswap V3 deposit (add liquidity) events."""
|
|
140
144
|
params: dict[str, Any] = {}
|
|
141
145
|
if symbol0:
|
|
142
146
|
params["symbol0"] = symbol0
|
|
@@ -144,15 +148,15 @@ class UniswapProtocol:
|
|
|
144
148
|
params["symbol1"] = symbol1
|
|
145
149
|
if fee:
|
|
146
150
|
params["fee"] = fee
|
|
147
|
-
return QueryBuilder(self._client, "/uniswap/events/
|
|
151
|
+
return QueryBuilder(self._client, "/uniswap/events/deposit", params)
|
|
148
152
|
|
|
149
|
-
def
|
|
153
|
+
def withdrawals(
|
|
150
154
|
self,
|
|
151
155
|
symbol0: str | None = None,
|
|
152
156
|
symbol1: str | None = None,
|
|
153
157
|
fee: int | None = None,
|
|
154
158
|
) -> QueryBuilder:
|
|
155
|
-
"""Start a query for Uniswap V3
|
|
159
|
+
"""Start a query for Uniswap V3 withdrawal (remove liquidity) events."""
|
|
156
160
|
params: dict[str, Any] = {}
|
|
157
161
|
if symbol0:
|
|
158
162
|
params["symbol0"] = symbol0
|
|
@@ -160,7 +164,23 @@ class UniswapProtocol:
|
|
|
160
164
|
params["symbol1"] = symbol1
|
|
161
165
|
if fee:
|
|
162
166
|
params["fee"] = fee
|
|
163
|
-
return QueryBuilder(self._client, "/uniswap/events/
|
|
167
|
+
return QueryBuilder(self._client, "/uniswap/events/withdraw", params)
|
|
168
|
+
|
|
169
|
+
def collects(
|
|
170
|
+
self,
|
|
171
|
+
symbol0: str | None = None,
|
|
172
|
+
symbol1: str | None = None,
|
|
173
|
+
fee: int | None = None,
|
|
174
|
+
) -> QueryBuilder:
|
|
175
|
+
"""Start a query for Uniswap V3 collect (fee collection) events."""
|
|
176
|
+
params: dict[str, Any] = {}
|
|
177
|
+
if symbol0:
|
|
178
|
+
params["symbol0"] = symbol0
|
|
179
|
+
if symbol1:
|
|
180
|
+
params["symbol1"] = symbol1
|
|
181
|
+
if fee:
|
|
182
|
+
params["fee"] = fee
|
|
183
|
+
return QueryBuilder(self._client, "/uniswap/events/collect", params)
|
|
164
184
|
|
|
165
185
|
|
|
166
186
|
class LidoProtocol:
|
|
@@ -169,17 +189,31 @@ class LidoProtocol:
|
|
|
169
189
|
def __init__(self, client: "BaseClient"):
|
|
170
190
|
self._client = client
|
|
171
191
|
|
|
192
|
+
# L1 ETH events
|
|
172
193
|
def deposits(self) -> QueryBuilder:
|
|
173
|
-
"""Start a query for Lido stETH deposit events."""
|
|
194
|
+
"""Start a query for Lido stETH deposit events (ETH L1 only)."""
|
|
174
195
|
return QueryBuilder(self._client, "/lido/events/deposit")
|
|
175
196
|
|
|
176
|
-
def
|
|
177
|
-
"""Start a query for Lido withdrawal events."""
|
|
178
|
-
return QueryBuilder(self._client, "/lido/events/
|
|
197
|
+
def withdrawal_requests(self) -> QueryBuilder:
|
|
198
|
+
"""Start a query for Lido withdrawal request events (ETH L1 only)."""
|
|
199
|
+
return QueryBuilder(self._client, "/lido/events/withdrawal_request")
|
|
200
|
+
|
|
201
|
+
def withdrawals_claimed(self) -> QueryBuilder:
|
|
202
|
+
"""Start a query for Lido claimed withdrawal events (ETH L1 only)."""
|
|
203
|
+
return QueryBuilder(self._client, "/lido/events/withdrawal_claimed")
|
|
204
|
+
|
|
205
|
+
# L2 events
|
|
206
|
+
def l2_deposits(self) -> QueryBuilder:
|
|
207
|
+
"""Start a query for Lido L2 deposit events (L2 networks only)."""
|
|
208
|
+
return QueryBuilder(self._client, "/lido/events/l2_deposit")
|
|
209
|
+
|
|
210
|
+
def l2_withdrawal_requests(self) -> QueryBuilder:
|
|
211
|
+
"""Start a query for Lido L2 withdrawal request events (L2 networks only)."""
|
|
212
|
+
return QueryBuilder(self._client, "/lido/events/l2_withdrawal_request")
|
|
179
213
|
|
|
180
214
|
|
|
181
215
|
class StaderProtocol:
|
|
182
|
-
"""Stader
|
|
216
|
+
"""Stader ETHx staking events with builder pattern (ETH only)."""
|
|
183
217
|
|
|
184
218
|
def __init__(self, client: "BaseClient"):
|
|
185
219
|
self._client = client
|
|
@@ -188,24 +222,36 @@ class StaderProtocol:
|
|
|
188
222
|
"""Start a query for Stader deposit events."""
|
|
189
223
|
return QueryBuilder(self._client, "/stader/events/deposit")
|
|
190
224
|
|
|
225
|
+
def withdrawal_requests(self) -> QueryBuilder:
|
|
226
|
+
"""Start a query for Stader withdrawal request events."""
|
|
227
|
+
return QueryBuilder(self._client, "/stader/events/withdrawal_request")
|
|
228
|
+
|
|
191
229
|
def withdrawals(self) -> QueryBuilder:
|
|
192
230
|
"""Start a query for Stader withdrawal events."""
|
|
193
|
-
return QueryBuilder(self._client, "/stader/events/
|
|
231
|
+
return QueryBuilder(self._client, "/stader/events/withdrawal")
|
|
194
232
|
|
|
195
233
|
|
|
196
234
|
class ThresholdProtocol:
|
|
197
|
-
"""Threshold tBTC events with builder pattern."""
|
|
235
|
+
"""Threshold tBTC events with builder pattern (ETH only)."""
|
|
198
236
|
|
|
199
237
|
def __init__(self, client: "BaseClient"):
|
|
200
238
|
self._client = client
|
|
201
239
|
|
|
202
|
-
def
|
|
203
|
-
"""Start a query for tBTC
|
|
204
|
-
return QueryBuilder(self._client, "/threshold/events/
|
|
240
|
+
def deposit_requests(self) -> QueryBuilder:
|
|
241
|
+
"""Start a query for tBTC deposit request (reveal) events."""
|
|
242
|
+
return QueryBuilder(self._client, "/threshold/events/deposit_request")
|
|
205
243
|
|
|
206
|
-
def
|
|
207
|
-
"""Start a query for tBTC
|
|
208
|
-
return QueryBuilder(self._client, "/threshold/events/
|
|
244
|
+
def deposits(self) -> QueryBuilder:
|
|
245
|
+
"""Start a query for tBTC deposit (mint) events."""
|
|
246
|
+
return QueryBuilder(self._client, "/threshold/events/deposit")
|
|
247
|
+
|
|
248
|
+
def withdrawal_requests(self) -> QueryBuilder:
|
|
249
|
+
"""Start a query for tBTC withdrawal request events."""
|
|
250
|
+
return QueryBuilder(self._client, "/threshold/events/withdrawal_request")
|
|
251
|
+
|
|
252
|
+
def withdrawals(self) -> QueryBuilder:
|
|
253
|
+
"""Start a query for tBTC withdrawal (unmint) events."""
|
|
254
|
+
return QueryBuilder(self._client, "/threshold/events/withdrawal")
|
|
209
255
|
|
|
210
256
|
|
|
211
257
|
# Async protocol implementations
|
|
@@ -220,11 +266,6 @@ class AsyncERC20Protocol:
|
|
|
220
266
|
params = {"token": token} if token else {}
|
|
221
267
|
return AsyncQueryBuilder(self._client, "/erc20/events/transfer", params)
|
|
222
268
|
|
|
223
|
-
def approvals(self, token: str | None = None) -> AsyncQueryBuilder:
|
|
224
|
-
"""Start a query for ERC20 approval events."""
|
|
225
|
-
params = {"token": token} if token else {}
|
|
226
|
-
return AsyncQueryBuilder(self._client, "/erc20/events/approval", params)
|
|
227
|
-
|
|
228
269
|
|
|
229
270
|
class AsyncNativeTokenProtocol:
|
|
230
271
|
"""Async native token events with builder pattern."""
|
|
@@ -259,6 +300,10 @@ class AsyncAAVEProtocol:
|
|
|
259
300
|
"""Start a query for AAVE repay events."""
|
|
260
301
|
return AsyncQueryBuilder(self._client, "/aave/events/repay")
|
|
261
302
|
|
|
303
|
+
def flashloans(self) -> AsyncQueryBuilder:
|
|
304
|
+
"""Start a query for AAVE flash loan events."""
|
|
305
|
+
return AsyncQueryBuilder(self._client, "/aave/events/flashloan")
|
|
306
|
+
|
|
262
307
|
def liquidations(self) -> AsyncQueryBuilder:
|
|
263
308
|
"""Start a query for AAVE liquidation events."""
|
|
264
309
|
return AsyncQueryBuilder(self._client, "/aave/events/liquidation")
|
|
@@ -286,13 +331,29 @@ class AsyncUniswapProtocol:
|
|
|
286
331
|
params["fee"] = fee
|
|
287
332
|
return AsyncQueryBuilder(self._client, "/uniswap/events/swap", params)
|
|
288
333
|
|
|
289
|
-
def
|
|
334
|
+
def deposits(
|
|
335
|
+
self,
|
|
336
|
+
symbol0: str | None = None,
|
|
337
|
+
symbol1: str | None = None,
|
|
338
|
+
fee: int | None = None,
|
|
339
|
+
) -> AsyncQueryBuilder:
|
|
340
|
+
"""Start a query for Uniswap V3 deposit events."""
|
|
341
|
+
params: dict[str, Any] = {}
|
|
342
|
+
if symbol0:
|
|
343
|
+
params["symbol0"] = symbol0
|
|
344
|
+
if symbol1:
|
|
345
|
+
params["symbol1"] = symbol1
|
|
346
|
+
if fee:
|
|
347
|
+
params["fee"] = fee
|
|
348
|
+
return AsyncQueryBuilder(self._client, "/uniswap/events/deposit", params)
|
|
349
|
+
|
|
350
|
+
def withdrawals(
|
|
290
351
|
self,
|
|
291
352
|
symbol0: str | None = None,
|
|
292
353
|
symbol1: str | None = None,
|
|
293
354
|
fee: int | None = None,
|
|
294
355
|
) -> AsyncQueryBuilder:
|
|
295
|
-
"""Start a query for Uniswap V3
|
|
356
|
+
"""Start a query for Uniswap V3 withdrawal events."""
|
|
296
357
|
params: dict[str, Any] = {}
|
|
297
358
|
if symbol0:
|
|
298
359
|
params["symbol0"] = symbol0
|
|
@@ -300,15 +361,15 @@ class AsyncUniswapProtocol:
|
|
|
300
361
|
params["symbol1"] = symbol1
|
|
301
362
|
if fee:
|
|
302
363
|
params["fee"] = fee
|
|
303
|
-
return AsyncQueryBuilder(self._client, "/uniswap/events/
|
|
364
|
+
return AsyncQueryBuilder(self._client, "/uniswap/events/withdraw", params)
|
|
304
365
|
|
|
305
|
-
def
|
|
366
|
+
def collects(
|
|
306
367
|
self,
|
|
307
368
|
symbol0: str | None = None,
|
|
308
369
|
symbol1: str | None = None,
|
|
309
370
|
fee: int | None = None,
|
|
310
371
|
) -> AsyncQueryBuilder:
|
|
311
|
-
"""Start a query for Uniswap V3
|
|
372
|
+
"""Start a query for Uniswap V3 collect events."""
|
|
312
373
|
params: dict[str, Any] = {}
|
|
313
374
|
if symbol0:
|
|
314
375
|
params["symbol0"] = symbol0
|
|
@@ -316,7 +377,7 @@ class AsyncUniswapProtocol:
|
|
|
316
377
|
params["symbol1"] = symbol1
|
|
317
378
|
if fee:
|
|
318
379
|
params["fee"] = fee
|
|
319
|
-
return AsyncQueryBuilder(self._client, "/uniswap/events/
|
|
380
|
+
return AsyncQueryBuilder(self._client, "/uniswap/events/collect", params)
|
|
320
381
|
|
|
321
382
|
|
|
322
383
|
class AsyncLidoProtocol:
|
|
@@ -326,12 +387,24 @@ class AsyncLidoProtocol:
|
|
|
326
387
|
self._client = client
|
|
327
388
|
|
|
328
389
|
def deposits(self) -> AsyncQueryBuilder:
|
|
329
|
-
"""Start a query for Lido deposit events."""
|
|
390
|
+
"""Start a query for Lido deposit events (ETH L1 only)."""
|
|
330
391
|
return AsyncQueryBuilder(self._client, "/lido/events/deposit")
|
|
331
392
|
|
|
332
|
-
def
|
|
333
|
-
"""Start a query for Lido withdrawal events."""
|
|
334
|
-
return AsyncQueryBuilder(self._client, "/lido/events/
|
|
393
|
+
def withdrawal_requests(self) -> AsyncQueryBuilder:
|
|
394
|
+
"""Start a query for Lido withdrawal request events (ETH L1 only)."""
|
|
395
|
+
return AsyncQueryBuilder(self._client, "/lido/events/withdrawal_request")
|
|
396
|
+
|
|
397
|
+
def withdrawals_claimed(self) -> AsyncQueryBuilder:
|
|
398
|
+
"""Start a query for Lido claimed withdrawal events (ETH L1 only)."""
|
|
399
|
+
return AsyncQueryBuilder(self._client, "/lido/events/withdrawal_claimed")
|
|
400
|
+
|
|
401
|
+
def l2_deposits(self) -> AsyncQueryBuilder:
|
|
402
|
+
"""Start a query for Lido L2 deposit events."""
|
|
403
|
+
return AsyncQueryBuilder(self._client, "/lido/events/l2_deposit")
|
|
404
|
+
|
|
405
|
+
def l2_withdrawal_requests(self) -> AsyncQueryBuilder:
|
|
406
|
+
"""Start a query for Lido L2 withdrawal request events."""
|
|
407
|
+
return AsyncQueryBuilder(self._client, "/lido/events/l2_withdrawal_request")
|
|
335
408
|
|
|
336
409
|
|
|
337
410
|
class AsyncStaderProtocol:
|
|
@@ -344,9 +417,13 @@ class AsyncStaderProtocol:
|
|
|
344
417
|
"""Start a query for Stader deposit events."""
|
|
345
418
|
return AsyncQueryBuilder(self._client, "/stader/events/deposit")
|
|
346
419
|
|
|
420
|
+
def withdrawal_requests(self) -> AsyncQueryBuilder:
|
|
421
|
+
"""Start a query for Stader withdrawal request events."""
|
|
422
|
+
return AsyncQueryBuilder(self._client, "/stader/events/withdrawal_request")
|
|
423
|
+
|
|
347
424
|
def withdrawals(self) -> AsyncQueryBuilder:
|
|
348
425
|
"""Start a query for Stader withdrawal events."""
|
|
349
|
-
return AsyncQueryBuilder(self._client, "/stader/events/
|
|
426
|
+
return AsyncQueryBuilder(self._client, "/stader/events/withdrawal")
|
|
350
427
|
|
|
351
428
|
|
|
352
429
|
class AsyncThresholdProtocol:
|
|
@@ -355,10 +432,18 @@ class AsyncThresholdProtocol:
|
|
|
355
432
|
def __init__(self, client: "BaseClient"):
|
|
356
433
|
self._client = client
|
|
357
434
|
|
|
358
|
-
def
|
|
359
|
-
"""Start a query for tBTC
|
|
360
|
-
return AsyncQueryBuilder(self._client, "/threshold/events/
|
|
435
|
+
def deposit_requests(self) -> AsyncQueryBuilder:
|
|
436
|
+
"""Start a query for tBTC deposit request events."""
|
|
437
|
+
return AsyncQueryBuilder(self._client, "/threshold/events/deposit_request")
|
|
361
438
|
|
|
362
|
-
def
|
|
363
|
-
"""Start a query for tBTC
|
|
364
|
-
return AsyncQueryBuilder(self._client, "/threshold/events/
|
|
439
|
+
def deposits(self) -> AsyncQueryBuilder:
|
|
440
|
+
"""Start a query for tBTC deposit events."""
|
|
441
|
+
return AsyncQueryBuilder(self._client, "/threshold/events/deposit")
|
|
442
|
+
|
|
443
|
+
def withdrawal_requests(self) -> AsyncQueryBuilder:
|
|
444
|
+
"""Start a query for tBTC withdrawal request events."""
|
|
445
|
+
return AsyncQueryBuilder(self._client, "/threshold/events/withdrawal_request")
|
|
446
|
+
|
|
447
|
+
def withdrawals(self) -> AsyncQueryBuilder:
|
|
448
|
+
"""Start a query for tBTC withdrawal events."""
|
|
449
|
+
return AsyncQueryBuilder(self._client, "/threshold/events/withdrawal")
|
defistream/query.py
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from .client import BaseClient
|
|
9
9
|
|
|
10
|
-
T = TypeVar("T")
|
|
11
|
-
|
|
12
10
|
|
|
13
11
|
class QueryBuilder:
|
|
14
12
|
"""
|
|
@@ -20,7 +18,7 @@ class QueryBuilder:
|
|
|
20
18
|
- as_file() - saves to CSV, Parquet, or JSON file
|
|
21
19
|
|
|
22
20
|
Example:
|
|
23
|
-
query = client.erc20.transfers("USDT").network("ETH").
|
|
21
|
+
query = client.erc20.transfers("USDT").network("ETH").block_range(21000000, 21010000)
|
|
24
22
|
query = query.min_amount(1000).sender("0x...")
|
|
25
23
|
df = query.as_df() # pandas DataFrame
|
|
26
24
|
df = query.as_df("polars") # polars DataFrame
|
|
@@ -51,7 +49,7 @@ class QueryBuilder:
|
|
|
51
49
|
|
|
52
50
|
# Network and block range
|
|
53
51
|
def network(self, network: str) -> "QueryBuilder":
|
|
54
|
-
"""Set the network (ETH, ARB, BASE, OP, etc.)."""
|
|
52
|
+
"""Set the network (ETH, ARB, BASE, OP, POLYGON, etc.)."""
|
|
55
53
|
return self._copy_with(network=network)
|
|
56
54
|
|
|
57
55
|
def start_block(self, block: int) -> "QueryBuilder":
|
|
@@ -79,13 +77,13 @@ class QueryBuilder:
|
|
|
79
77
|
"""Set both start and end times."""
|
|
80
78
|
return self._copy_with(since=start, until=end)
|
|
81
79
|
|
|
82
|
-
#
|
|
80
|
+
# ERC20 and Native Token filters
|
|
83
81
|
def sender(self, address: str) -> "QueryBuilder":
|
|
84
|
-
"""Filter by sender address."""
|
|
82
|
+
"""Filter by sender address (ERC20, Native Token)."""
|
|
85
83
|
return self._copy_with(sender=address)
|
|
86
84
|
|
|
87
85
|
def receiver(self, address: str) -> "QueryBuilder":
|
|
88
|
-
"""Filter by receiver address."""
|
|
86
|
+
"""Filter by receiver address (ERC20, Native Token)."""
|
|
89
87
|
return self._copy_with(receiver=address)
|
|
90
88
|
|
|
91
89
|
def from_address(self, address: str) -> "QueryBuilder":
|
|
@@ -97,52 +95,40 @@ class QueryBuilder:
|
|
|
97
95
|
return self.receiver(address)
|
|
98
96
|
|
|
99
97
|
def min_amount(self, amount: float) -> "QueryBuilder":
|
|
100
|
-
"""Filter by minimum amount."""
|
|
98
|
+
"""Filter by minimum amount (ERC20, Native Token)."""
|
|
101
99
|
return self._copy_with(min_amount=amount)
|
|
102
100
|
|
|
101
|
+
def max_amount(self, amount: float) -> "QueryBuilder":
|
|
102
|
+
"""Filter by maximum amount (ERC20, Native Token)."""
|
|
103
|
+
return self._copy_with(max_amount=amount)
|
|
104
|
+
|
|
103
105
|
# ERC20 specific
|
|
104
106
|
def token(self, symbol: str) -> "QueryBuilder":
|
|
105
|
-
"""Set token symbol (
|
|
107
|
+
"""Set token symbol or address (ERC20)."""
|
|
106
108
|
return self._copy_with(token=symbol)
|
|
107
109
|
|
|
108
|
-
def
|
|
109
|
-
"""
|
|
110
|
-
return self._copy_with(
|
|
111
|
-
|
|
112
|
-
def spender(self, address: str) -> "QueryBuilder":
|
|
113
|
-
"""Filter by spender address (for approvals)."""
|
|
114
|
-
return self._copy_with(spender=address)
|
|
110
|
+
def decimals(self, decimals: int) -> "QueryBuilder":
|
|
111
|
+
"""Set token decimals when using custom address (ERC20). Default: 18."""
|
|
112
|
+
return self._copy_with(decimals=decimals)
|
|
115
113
|
|
|
116
114
|
# AAVE specific
|
|
117
|
-
def
|
|
118
|
-
"""
|
|
119
|
-
return self._copy_with(
|
|
120
|
-
|
|
121
|
-
def reserve(self, address: str) -> "QueryBuilder":
|
|
122
|
-
"""Filter by reserve address (for AAVE)."""
|
|
123
|
-
return self._copy_with(reserve=address)
|
|
124
|
-
|
|
125
|
-
def liquidator(self, address: str) -> "QueryBuilder":
|
|
126
|
-
"""Filter by liquidator address (for AAVE liquidations)."""
|
|
127
|
-
return self._copy_with(liquidator=address)
|
|
115
|
+
def eth_market_type(self, market_type: str) -> "QueryBuilder":
|
|
116
|
+
"""Set AAVE market type for ETH network: 'Core', 'Prime', or 'EtherFi'. Default: 'Core'."""
|
|
117
|
+
return self._copy_with(eth_market_type=market_type)
|
|
128
118
|
|
|
129
119
|
# Uniswap specific
|
|
130
120
|
def symbol0(self, symbol: str) -> "QueryBuilder":
|
|
131
|
-
"""Set first token symbol (
|
|
121
|
+
"""Set first token symbol (Uniswap)."""
|
|
132
122
|
return self._copy_with(symbol0=symbol)
|
|
133
123
|
|
|
134
124
|
def symbol1(self, symbol: str) -> "QueryBuilder":
|
|
135
|
-
"""Set second token symbol (
|
|
125
|
+
"""Set second token symbol (Uniswap)."""
|
|
136
126
|
return self._copy_with(symbol1=symbol)
|
|
137
127
|
|
|
138
128
|
def fee(self, fee_tier: int) -> "QueryBuilder":
|
|
139
|
-
"""Set fee tier (
|
|
129
|
+
"""Set fee tier (Uniswap): 100, 500, 3000, 10000."""
|
|
140
130
|
return self._copy_with(fee=fee_tier)
|
|
141
131
|
|
|
142
|
-
def pool(self, address: str) -> "QueryBuilder":
|
|
143
|
-
"""Set pool address (for Uniswap)."""
|
|
144
|
-
return self._copy_with(pool=address)
|
|
145
|
-
|
|
146
132
|
# Verbose mode
|
|
147
133
|
def verbose(self, enabled: bool = True) -> "QueryBuilder":
|
|
148
134
|
"""Include all metadata fields (tx_hash, tx_id, log_index, network, name)."""
|
|
@@ -161,7 +147,7 @@ class QueryBuilder:
|
|
|
161
147
|
"""
|
|
162
148
|
Execute query and return results as list of dictionaries.
|
|
163
149
|
|
|
164
|
-
Uses JSON format from API.
|
|
150
|
+
Uses JSON format from API. Limited to 10,000 blocks.
|
|
165
151
|
|
|
166
152
|
Returns:
|
|
167
153
|
List of event dictionaries
|
|
@@ -253,7 +239,7 @@ class AsyncQueryBuilder:
|
|
|
253
239
|
- as_file() - saves to CSV, Parquet, or JSON file
|
|
254
240
|
|
|
255
241
|
Example:
|
|
256
|
-
query = client.erc20.transfers("USDT").network("ETH").
|
|
242
|
+
query = client.erc20.transfers("USDT").network("ETH").block_range(21000000, 21010000)
|
|
257
243
|
df = await query.as_df() # pandas DataFrame
|
|
258
244
|
df = await query.as_df("polars") # polars DataFrame
|
|
259
245
|
await query.as_file("transfers.csv") # save to CSV
|
|
@@ -283,7 +269,7 @@ class AsyncQueryBuilder:
|
|
|
283
269
|
|
|
284
270
|
# Network and block range
|
|
285
271
|
def network(self, network: str) -> "AsyncQueryBuilder":
|
|
286
|
-
"""Set the network (ETH, ARB, BASE, OP, etc.)."""
|
|
272
|
+
"""Set the network (ETH, ARB, BASE, OP, POLYGON, etc.)."""
|
|
287
273
|
return self._copy_with(network=network)
|
|
288
274
|
|
|
289
275
|
def start_block(self, block: int) -> "AsyncQueryBuilder":
|
|
@@ -311,13 +297,13 @@ class AsyncQueryBuilder:
|
|
|
311
297
|
"""Set both start and end times."""
|
|
312
298
|
return self._copy_with(since=start, until=end)
|
|
313
299
|
|
|
314
|
-
#
|
|
300
|
+
# ERC20 and Native Token filters
|
|
315
301
|
def sender(self, address: str) -> "AsyncQueryBuilder":
|
|
316
|
-
"""Filter by sender address."""
|
|
302
|
+
"""Filter by sender address (ERC20, Native Token)."""
|
|
317
303
|
return self._copy_with(sender=address)
|
|
318
304
|
|
|
319
305
|
def receiver(self, address: str) -> "AsyncQueryBuilder":
|
|
320
|
-
"""Filter by receiver address."""
|
|
306
|
+
"""Filter by receiver address (ERC20, Native Token)."""
|
|
321
307
|
return self._copy_with(receiver=address)
|
|
322
308
|
|
|
323
309
|
def from_address(self, address: str) -> "AsyncQueryBuilder":
|
|
@@ -329,52 +315,40 @@ class AsyncQueryBuilder:
|
|
|
329
315
|
return self.receiver(address)
|
|
330
316
|
|
|
331
317
|
def min_amount(self, amount: float) -> "AsyncQueryBuilder":
|
|
332
|
-
"""Filter by minimum amount."""
|
|
318
|
+
"""Filter by minimum amount (ERC20, Native Token)."""
|
|
333
319
|
return self._copy_with(min_amount=amount)
|
|
334
320
|
|
|
321
|
+
def max_amount(self, amount: float) -> "AsyncQueryBuilder":
|
|
322
|
+
"""Filter by maximum amount (ERC20, Native Token)."""
|
|
323
|
+
return self._copy_with(max_amount=amount)
|
|
324
|
+
|
|
335
325
|
# ERC20 specific
|
|
336
326
|
def token(self, symbol: str) -> "AsyncQueryBuilder":
|
|
337
|
-
"""Set token symbol (
|
|
327
|
+
"""Set token symbol or address (ERC20)."""
|
|
338
328
|
return self._copy_with(token=symbol)
|
|
339
329
|
|
|
340
|
-
def
|
|
341
|
-
"""
|
|
342
|
-
return self._copy_with(
|
|
343
|
-
|
|
344
|
-
def spender(self, address: str) -> "AsyncQueryBuilder":
|
|
345
|
-
"""Filter by spender address (for approvals)."""
|
|
346
|
-
return self._copy_with(spender=address)
|
|
330
|
+
def decimals(self, decimals: int) -> "AsyncQueryBuilder":
|
|
331
|
+
"""Set token decimals when using custom address (ERC20). Default: 18."""
|
|
332
|
+
return self._copy_with(decimals=decimals)
|
|
347
333
|
|
|
348
334
|
# AAVE specific
|
|
349
|
-
def
|
|
350
|
-
"""
|
|
351
|
-
return self._copy_with(
|
|
352
|
-
|
|
353
|
-
def reserve(self, address: str) -> "AsyncQueryBuilder":
|
|
354
|
-
"""Filter by reserve address (for AAVE)."""
|
|
355
|
-
return self._copy_with(reserve=address)
|
|
356
|
-
|
|
357
|
-
def liquidator(self, address: str) -> "AsyncQueryBuilder":
|
|
358
|
-
"""Filter by liquidator address (for AAVE liquidations)."""
|
|
359
|
-
return self._copy_with(liquidator=address)
|
|
335
|
+
def eth_market_type(self, market_type: str) -> "AsyncQueryBuilder":
|
|
336
|
+
"""Set AAVE market type for ETH network: 'Core', 'Prime', or 'EtherFi'. Default: 'Core'."""
|
|
337
|
+
return self._copy_with(eth_market_type=market_type)
|
|
360
338
|
|
|
361
339
|
# Uniswap specific
|
|
362
340
|
def symbol0(self, symbol: str) -> "AsyncQueryBuilder":
|
|
363
|
-
"""Set first token symbol (
|
|
341
|
+
"""Set first token symbol (Uniswap)."""
|
|
364
342
|
return self._copy_with(symbol0=symbol)
|
|
365
343
|
|
|
366
344
|
def symbol1(self, symbol: str) -> "AsyncQueryBuilder":
|
|
367
|
-
"""Set second token symbol (
|
|
345
|
+
"""Set second token symbol (Uniswap)."""
|
|
368
346
|
return self._copy_with(symbol1=symbol)
|
|
369
347
|
|
|
370
348
|
def fee(self, fee_tier: int) -> "AsyncQueryBuilder":
|
|
371
|
-
"""Set fee tier (
|
|
349
|
+
"""Set fee tier (Uniswap): 100, 500, 3000, 10000."""
|
|
372
350
|
return self._copy_with(fee=fee_tier)
|
|
373
351
|
|
|
374
|
-
def pool(self, address: str) -> "AsyncQueryBuilder":
|
|
375
|
-
"""Set pool address (for Uniswap)."""
|
|
376
|
-
return self._copy_with(pool=address)
|
|
377
|
-
|
|
378
352
|
# Verbose mode
|
|
379
353
|
def verbose(self, enabled: bool = True) -> "AsyncQueryBuilder":
|
|
380
354
|
"""Include all metadata fields (tx_hash, tx_id, log_index, network, name)."""
|
|
@@ -393,7 +367,7 @@ class AsyncQueryBuilder:
|
|
|
393
367
|
"""
|
|
394
368
|
Execute query and return results as list of dictionaries.
|
|
395
369
|
|
|
396
|
-
Uses JSON format from API.
|
|
370
|
+
Uses JSON format from API. Limited to 10,000 blocks.
|
|
397
371
|
|
|
398
372
|
Returns:
|
|
399
373
|
List of event dictionaries
|
|
@@ -405,8 +379,6 @@ class AsyncQueryBuilder:
|
|
|
405
379
|
"""
|
|
406
380
|
Execute query and return results as DataFrame.
|
|
407
381
|
|
|
408
|
-
Uses Parquet format from API for efficiency, then converts to DataFrame.
|
|
409
|
-
|
|
410
382
|
Args:
|
|
411
383
|
library: DataFrame library to use - "pandas" (default) or "polars"
|
|
412
384
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: defistream
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Python client for the DeFiStream API
|
|
5
5
|
Project-URL: Homepage, https://defistream.dev
|
|
6
6
|
Project-URL: Documentation, https://docs.defistream.dev
|
|
@@ -34,7 +34,11 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
|
|
35
35
|
# DeFiStream Python Client
|
|
36
36
|
|
|
37
|
-
Official Python client for the [DeFiStream API](https://defistream.dev)
|
|
37
|
+
Official Python client for the [DeFiStream API](https://defistream.dev).
|
|
38
|
+
|
|
39
|
+
## Getting an API Key
|
|
40
|
+
|
|
41
|
+
To use the DeFiStream API, you need to sign up for an account at [defistream.dev](https://defistream.dev) to obtain your API key.
|
|
38
42
|
|
|
39
43
|
## Installation
|
|
40
44
|
|
|
@@ -61,44 +65,41 @@ client = DeFiStream()
|
|
|
61
65
|
client = DeFiStream(api_key="dsk_your_api_key")
|
|
62
66
|
|
|
63
67
|
# Query ERC20 transfers using builder pattern
|
|
64
|
-
|
|
68
|
+
df = (
|
|
65
69
|
client.erc20.transfers("USDT")
|
|
66
70
|
.network("ETH")
|
|
67
|
-
.
|
|
68
|
-
.
|
|
69
|
-
.as_dict()
|
|
71
|
+
.block_range(21000000, 21010000)
|
|
72
|
+
.as_df()
|
|
70
73
|
)
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
print(f"{transfer['sender']} -> {transfer['receiver']}: {transfer['amount']}")
|
|
75
|
+
print(df.head())
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
## Features
|
|
77
79
|
|
|
78
80
|
- **Builder pattern**: Fluent query API with chainable methods
|
|
79
81
|
- **Type-safe**: Full type hints and Pydantic models
|
|
80
|
-
- **Multiple formats**:
|
|
82
|
+
- **Multiple formats**: DataFrame (pandas/polars), CSV, Parquet, JSON
|
|
81
83
|
- **Async support**: Native async/await with `AsyncDeFiStream`
|
|
82
|
-
- **All protocols**: AAVE, Uniswap, Lido,
|
|
83
|
-
- **Verbose mode**: Include all metadata fields (tx_hash, tx_id, log_index, network, name)
|
|
84
|
+
- **All protocols**: ERC20, AAVE, Uniswap, Lido, Stader, Threshold, Native tokens
|
|
84
85
|
|
|
85
86
|
## Supported Protocols
|
|
86
87
|
|
|
87
88
|
| Protocol | Events |
|
|
88
89
|
|----------|--------|
|
|
89
|
-
| ERC20 | `transfers
|
|
90
|
+
| ERC20 | `transfers` |
|
|
90
91
|
| Native Token | `transfers` |
|
|
91
|
-
| AAVE V3 | `deposits`, `withdrawals`, `borrows`, `repays`, `liquidations` |
|
|
92
|
-
| Uniswap V3 | `swaps`, `
|
|
93
|
-
| Lido | `deposits`, `
|
|
94
|
-
| Stader | `deposits`, `withdrawals` |
|
|
95
|
-
| Threshold | `
|
|
92
|
+
| AAVE V3 | `deposits`, `withdrawals`, `borrows`, `repays`, `flashloans`, `liquidations` |
|
|
93
|
+
| Uniswap V3 | `swaps`, `deposits`, `withdrawals`, `collects` |
|
|
94
|
+
| Lido | `deposits`, `withdrawal_requests`, `withdrawals_claimed`, `l2_deposits`, `l2_withdrawal_requests` |
|
|
95
|
+
| Stader | `deposits`, `withdrawal_requests`, `withdrawals` |
|
|
96
|
+
| Threshold | `deposit_requests`, `deposits`, `withdrawal_requests`, `withdrawals` |
|
|
96
97
|
|
|
97
98
|
## Usage Examples
|
|
98
99
|
|
|
99
100
|
### Builder Pattern
|
|
100
101
|
|
|
101
|
-
The client uses a fluent builder pattern. The query is only executed when you call a terminal method like `
|
|
102
|
+
The client uses a fluent builder pattern. The query is only executed when you call a terminal method like `as_df()`, `as_file()`, or `as_dict()`.
|
|
102
103
|
|
|
103
104
|
```python
|
|
104
105
|
from defistream import DeFiStream
|
|
@@ -108,21 +109,19 @@ client = DeFiStream()
|
|
|
108
109
|
# Build query step by step
|
|
109
110
|
query = client.erc20.transfers("USDT")
|
|
110
111
|
query = query.network("ETH")
|
|
111
|
-
query = query.
|
|
112
|
-
query = query.end_block(24100000)
|
|
112
|
+
query = query.block_range(21000000, 21010000)
|
|
113
113
|
query = query.min_amount(1000)
|
|
114
114
|
|
|
115
|
-
# Execute and get
|
|
116
|
-
|
|
115
|
+
# Execute and get DataFrame
|
|
116
|
+
df = query.as_df()
|
|
117
117
|
|
|
118
118
|
# Or chain everything
|
|
119
|
-
|
|
119
|
+
df = (
|
|
120
120
|
client.erc20.transfers("USDT")
|
|
121
121
|
.network("ETH")
|
|
122
|
-
.
|
|
123
|
-
.end_block(24100000)
|
|
122
|
+
.block_range(21000000, 21010000)
|
|
124
123
|
.min_amount(1000)
|
|
125
|
-
.
|
|
124
|
+
.as_df()
|
|
126
125
|
)
|
|
127
126
|
```
|
|
128
127
|
|
|
@@ -130,35 +129,21 @@ transfers = (
|
|
|
130
129
|
|
|
131
130
|
```python
|
|
132
131
|
# Get USDT transfers over 10,000 USDT
|
|
133
|
-
|
|
132
|
+
df = (
|
|
134
133
|
client.erc20.transfers("USDT")
|
|
135
134
|
.network("ETH")
|
|
136
|
-
.
|
|
137
|
-
.end_block(24100000)
|
|
135
|
+
.block_range(21000000, 21010000)
|
|
138
136
|
.min_amount(10000)
|
|
139
|
-
.
|
|
137
|
+
.as_df()
|
|
140
138
|
)
|
|
141
139
|
|
|
142
140
|
# Filter by sender
|
|
143
|
-
|
|
141
|
+
df = (
|
|
144
142
|
client.erc20.transfers("USDT")
|
|
145
143
|
.network("ETH")
|
|
146
|
-
.
|
|
147
|
-
.end_block(24100000)
|
|
144
|
+
.block_range(21000000, 21010000)
|
|
148
145
|
.sender("0x28c6c06298d514db089934071355e5743bf21d60")
|
|
149
|
-
.
|
|
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()
|
|
146
|
+
.as_df()
|
|
162
147
|
)
|
|
163
148
|
```
|
|
164
149
|
|
|
@@ -166,22 +151,20 @@ transfers = (
|
|
|
166
151
|
|
|
167
152
|
```python
|
|
168
153
|
# Get deposits
|
|
169
|
-
|
|
154
|
+
df = (
|
|
170
155
|
client.aave.deposits()
|
|
171
156
|
.network("ETH")
|
|
172
|
-
.
|
|
173
|
-
.
|
|
174
|
-
.as_dict()
|
|
157
|
+
.block_range(21000000, 21010000)
|
|
158
|
+
.as_df()
|
|
175
159
|
)
|
|
176
160
|
|
|
177
|
-
#
|
|
178
|
-
|
|
179
|
-
client.aave.
|
|
161
|
+
# Use a specific market type on ETH (Core, Prime, or EtherFi)
|
|
162
|
+
df = (
|
|
163
|
+
client.aave.deposits()
|
|
180
164
|
.network("ETH")
|
|
181
|
-
.
|
|
182
|
-
.
|
|
183
|
-
.
|
|
184
|
-
.as_dict()
|
|
165
|
+
.block_range(21000000, 21010000)
|
|
166
|
+
.eth_market_type("Prime")
|
|
167
|
+
.as_df()
|
|
185
168
|
)
|
|
186
169
|
```
|
|
187
170
|
|
|
@@ -189,24 +172,22 @@ liquidations = (
|
|
|
189
172
|
|
|
190
173
|
```python
|
|
191
174
|
# Get swaps for WETH/USDC pool with 0.05% fee tier
|
|
192
|
-
|
|
175
|
+
df = (
|
|
193
176
|
client.uniswap.swaps("WETH", "USDC", 500)
|
|
194
177
|
.network("ETH")
|
|
195
|
-
.
|
|
196
|
-
.
|
|
197
|
-
.as_dict()
|
|
178
|
+
.block_range(21000000, 21010000)
|
|
179
|
+
.as_df()
|
|
198
180
|
)
|
|
199
181
|
|
|
200
182
|
# Or build with chain methods
|
|
201
|
-
|
|
183
|
+
df = (
|
|
202
184
|
client.uniswap.swaps()
|
|
203
185
|
.symbol0("WETH")
|
|
204
186
|
.symbol1("USDC")
|
|
205
187
|
.fee(500)
|
|
206
188
|
.network("ETH")
|
|
207
|
-
.
|
|
208
|
-
.
|
|
209
|
-
.as_dict()
|
|
189
|
+
.block_range(21000000, 21010000)
|
|
190
|
+
.as_df()
|
|
210
191
|
)
|
|
211
192
|
```
|
|
212
193
|
|
|
@@ -214,13 +195,12 @@ swaps = (
|
|
|
214
195
|
|
|
215
196
|
```python
|
|
216
197
|
# Get ETH transfers >= 1 ETH
|
|
217
|
-
|
|
198
|
+
df = (
|
|
218
199
|
client.native_token.transfers()
|
|
219
200
|
.network("ETH")
|
|
220
|
-
.
|
|
221
|
-
.end_block(24100000)
|
|
201
|
+
.block_range(21000000, 21010000)
|
|
222
202
|
.min_amount(1.0)
|
|
223
|
-
.
|
|
203
|
+
.as_df()
|
|
224
204
|
)
|
|
225
205
|
```
|
|
226
206
|
|
|
@@ -230,25 +210,21 @@ By default, responses omit metadata fields to reduce payload size. Use `.verbose
|
|
|
230
210
|
|
|
231
211
|
```python
|
|
232
212
|
# Default: compact response (no tx_hash, tx_id, log_index, network, name)
|
|
233
|
-
|
|
213
|
+
df = (
|
|
234
214
|
client.erc20.transfers("USDT")
|
|
235
215
|
.network("ETH")
|
|
236
|
-
.
|
|
237
|
-
.
|
|
238
|
-
.as_dict()
|
|
216
|
+
.block_range(21000000, 21010000)
|
|
217
|
+
.as_df()
|
|
239
218
|
)
|
|
240
|
-
# Returns: [{"block_number": 24000050, "sender": "0x...", "receiver": "0x...", "amount": 1000.0, ...}]
|
|
241
219
|
|
|
242
220
|
# Verbose: includes all metadata fields
|
|
243
|
-
|
|
221
|
+
df = (
|
|
244
222
|
client.erc20.transfers("USDT")
|
|
245
223
|
.network("ETH")
|
|
246
|
-
.
|
|
247
|
-
.end_block(24100000)
|
|
224
|
+
.block_range(21000000, 21010000)
|
|
248
225
|
.verbose()
|
|
249
|
-
.
|
|
226
|
+
.as_df()
|
|
250
227
|
)
|
|
251
|
-
# Returns: [{"name": "TransferEvent", "network": "ETH", "tx_id": "0x...", "tx_hash": "0x...", "log_index": 5, "block_number": 24000050, ...}]
|
|
252
228
|
```
|
|
253
229
|
|
|
254
230
|
### Return as DataFrame
|
|
@@ -258,8 +234,7 @@ transfers = (
|
|
|
258
234
|
df = (
|
|
259
235
|
client.erc20.transfers("USDT")
|
|
260
236
|
.network("ETH")
|
|
261
|
-
.
|
|
262
|
-
.end_block(24100000)
|
|
237
|
+
.block_range(21000000, 21010000)
|
|
263
238
|
.as_df()
|
|
264
239
|
)
|
|
265
240
|
|
|
@@ -267,8 +242,7 @@ df = (
|
|
|
267
242
|
df = (
|
|
268
243
|
client.erc20.transfers("USDT")
|
|
269
244
|
.network("ETH")
|
|
270
|
-
.
|
|
271
|
-
.end_block(24100000)
|
|
245
|
+
.block_range(21000000, 21010000)
|
|
272
246
|
.as_df("polars")
|
|
273
247
|
)
|
|
274
248
|
```
|
|
@@ -278,43 +252,49 @@ df = (
|
|
|
278
252
|
Format is automatically determined by file extension:
|
|
279
253
|
|
|
280
254
|
```python
|
|
281
|
-
# Save as
|
|
255
|
+
# Save as Parquet (recommended for large datasets)
|
|
282
256
|
(
|
|
283
257
|
client.erc20.transfers("USDT")
|
|
284
258
|
.network("ETH")
|
|
285
|
-
.
|
|
286
|
-
.
|
|
287
|
-
.as_file("transfers.csv")
|
|
259
|
+
.block_range(21000000, 21100000)
|
|
260
|
+
.as_file("transfers.parquet")
|
|
288
261
|
)
|
|
289
262
|
|
|
290
|
-
# Save as
|
|
263
|
+
# Save as CSV
|
|
291
264
|
(
|
|
292
265
|
client.erc20.transfers("USDT")
|
|
293
266
|
.network("ETH")
|
|
294
|
-
.
|
|
295
|
-
.
|
|
296
|
-
.as_file("transfers.parquet")
|
|
267
|
+
.block_range(21000000, 21100000)
|
|
268
|
+
.as_file("transfers.csv")
|
|
297
269
|
)
|
|
298
270
|
|
|
299
271
|
# Save as JSON
|
|
300
272
|
(
|
|
301
273
|
client.erc20.transfers("USDT")
|
|
302
274
|
.network("ETH")
|
|
303
|
-
.
|
|
304
|
-
.end_block(24100000)
|
|
275
|
+
.block_range(21000000, 21010000)
|
|
305
276
|
.as_file("transfers.json")
|
|
306
277
|
)
|
|
278
|
+
```
|
|
307
279
|
|
|
308
|
-
|
|
309
|
-
|
|
280
|
+
### Return as Dictionary (JSON)
|
|
281
|
+
|
|
282
|
+
For small queries, you can get results as a list of dictionaries:
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
transfers = (
|
|
310
286
|
client.erc20.transfers("USDT")
|
|
311
287
|
.network("ETH")
|
|
312
|
-
.
|
|
313
|
-
.
|
|
314
|
-
.as_file("transfers", format="csv")
|
|
288
|
+
.block_range(21000000, 21010000)
|
|
289
|
+
.as_dict()
|
|
315
290
|
)
|
|
291
|
+
|
|
292
|
+
for transfer in transfers:
|
|
293
|
+
print(f"{transfer['sender']} -> {transfer['receiver']}: {transfer['amount']}")
|
|
316
294
|
```
|
|
317
295
|
|
|
296
|
+
> **Note:** `as_dict()` and `as_file("*.json")` use JSON format which has a **10,000 block limit**. For larger block ranges, use `as_df()` or `as_file()` with `.parquet` or `.csv` extensions, which support up to 1,000,000 blocks.
|
|
297
|
+
|
|
318
298
|
### Async Usage
|
|
319
299
|
|
|
320
300
|
```python
|
|
@@ -323,41 +303,17 @@ from defistream import AsyncDeFiStream
|
|
|
323
303
|
|
|
324
304
|
async def main():
|
|
325
305
|
async with AsyncDeFiStream() as client:
|
|
326
|
-
|
|
306
|
+
df = await (
|
|
327
307
|
client.erc20.transfers("USDT")
|
|
328
308
|
.network("ETH")
|
|
329
|
-
.
|
|
330
|
-
.
|
|
331
|
-
.as_dict()
|
|
309
|
+
.block_range(21000000, 21010000)
|
|
310
|
+
.as_df()
|
|
332
311
|
)
|
|
333
|
-
print(f"Found {len(
|
|
312
|
+
print(f"Found {len(df)} transfers")
|
|
334
313
|
|
|
335
314
|
asyncio.run(main())
|
|
336
315
|
```
|
|
337
316
|
|
|
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
317
|
## Configuration
|
|
362
318
|
|
|
363
319
|
### Environment Variables
|
|
@@ -402,12 +358,11 @@ from defistream.exceptions import (
|
|
|
402
358
|
client = DeFiStream()
|
|
403
359
|
|
|
404
360
|
try:
|
|
405
|
-
|
|
361
|
+
df = (
|
|
406
362
|
client.erc20.transfers("USDT")
|
|
407
363
|
.network("ETH")
|
|
408
|
-
.
|
|
409
|
-
.
|
|
410
|
-
.as_dict()
|
|
364
|
+
.block_range(21000000, 21010000)
|
|
365
|
+
.as_df()
|
|
411
366
|
)
|
|
412
367
|
except AuthenticationError:
|
|
413
368
|
print("Invalid API key")
|
|
@@ -426,12 +381,11 @@ except DeFiStreamError as e:
|
|
|
426
381
|
Access rate limit and quota information:
|
|
427
382
|
|
|
428
383
|
```python
|
|
429
|
-
|
|
384
|
+
df = (
|
|
430
385
|
client.erc20.transfers("USDT")
|
|
431
386
|
.network("ETH")
|
|
432
|
-
.
|
|
433
|
-
.
|
|
434
|
-
.as_dict()
|
|
387
|
+
.block_range(21000000, 21010000)
|
|
388
|
+
.as_df()
|
|
435
389
|
)
|
|
436
390
|
|
|
437
391
|
# Access response metadata
|
|
@@ -446,7 +400,7 @@ print(f"Request cost: {client.last_response.request_cost}")
|
|
|
446
400
|
|
|
447
401
|
| Method | Description |
|
|
448
402
|
|--------|-------------|
|
|
449
|
-
| `.network(net)` | Set network (ETH, ARB, BASE, OP, etc.) |
|
|
403
|
+
| `.network(net)` | Set network (ETH, ARB, BASE, OP, POLYGON, etc.) |
|
|
450
404
|
| `.start_block(n)` | Set starting block number |
|
|
451
405
|
| `.end_block(n)` | Set ending block number |
|
|
452
406
|
| `.block_range(start, end)` | Set both start and end blocks |
|
|
@@ -455,35 +409,30 @@ print(f"Request cost: {client.last_response.request_cost}")
|
|
|
455
409
|
| `.time_range(start, end)` | Set both start and end times |
|
|
456
410
|
| `.verbose()` | Include all metadata fields |
|
|
457
411
|
|
|
458
|
-
###
|
|
412
|
+
### Protocol-Specific Parameters
|
|
459
413
|
|
|
460
414
|
| Method | Protocols | Description |
|
|
461
415
|
|--------|-----------|-------------|
|
|
416
|
+
| `.token(symbol)` | ERC20 | Token symbol (USDT, USDC) or contract address |
|
|
417
|
+
| `.decimals(n)` | ERC20 | Token decimals when using custom address (default: 18) |
|
|
462
418
|
| `.sender(addr)` | ERC20, Native | Filter by sender address |
|
|
463
419
|
| `.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
420
|
| `.min_amount(amt)` | ERC20, Native | Minimum transfer amount |
|
|
467
|
-
| `.
|
|
468
|
-
| `.
|
|
469
|
-
| `.
|
|
470
|
-
| `.
|
|
471
|
-
| `.
|
|
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 |
|
|
421
|
+
| `.max_amount(amt)` | ERC20, Native | Maximum transfer amount |
|
|
422
|
+
| `.eth_market_type(type)` | AAVE | Market type for ETH: 'Core', 'Prime', 'EtherFi' |
|
|
423
|
+
| `.symbol0(sym)` | Uniswap | First token symbol (required) |
|
|
424
|
+
| `.symbol1(sym)` | Uniswap | Second token symbol (required) |
|
|
425
|
+
| `.fee(tier)` | Uniswap | Fee tier: 100, 500, 3000, 10000 (required) |
|
|
477
426
|
|
|
478
427
|
### Terminal Methods
|
|
479
428
|
|
|
480
429
|
| Method | Description |
|
|
481
430
|
|--------|-------------|
|
|
482
|
-
| `.as_dict()` | Execute and return list of dicts (JSON) |
|
|
483
431
|
| `.as_df()` | Execute and return pandas DataFrame |
|
|
484
432
|
| `.as_df("polars")` | Execute and return polars DataFrame |
|
|
485
433
|
| `.as_file(path)` | Execute and save to file (format from extension) |
|
|
486
434
|
| `.as_file(path, format="csv")` | Execute and save with explicit format |
|
|
435
|
+
| `.as_dict()` | Execute and return list of dicts (JSON, 10K block limit) |
|
|
487
436
|
|
|
488
437
|
## License
|
|
489
438
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
defistream/__init__.py,sha256=RUAlky12Gxi0hYBUeXJ32qkhku-BLs8cOqZCpC6nZE0,1680
|
|
2
|
+
defistream/client.py,sha256=Ku8ouDbM6Mx4lVmqvBwNvNt-h2FkvqauPMSjKyPkjU4,12717
|
|
3
|
+
defistream/exceptions.py,sha256=_GxZQ18_YvXFtmNHeddWV8fHPIllHgFeP7fP0CmHF1k,1492
|
|
4
|
+
defistream/models.py,sha256=JiG4IUq19HIANz9MHQCkaE6onGNyNzXcbdJmosrHsw0,3812
|
|
5
|
+
defistream/protocols.py,sha256=5_bYd46lDy-mK6LZ8sTGW0Z2IVH4g0cQ5rBbbOXsw0U,15368
|
|
6
|
+
defistream/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
defistream/query.py,sha256=cOGba8MQNFKXTs39csD81vrMW76c9O3-S40R0KAMyCA,17158
|
|
8
|
+
defistream-1.0.2.dist-info/METADATA,sha256=MSChxyWWx6vCUALDfZFybRLcqJsKtyi56N4eY-i2vyE,10950
|
|
9
|
+
defistream-1.0.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
10
|
+
defistream-1.0.2.dist-info/licenses/LICENSE,sha256=72DWAof8dMePfFQmfaswClW5d-sE6k7p-7VpuSKLmU4,1067
|
|
11
|
+
defistream-1.0.2.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
defistream/__init__.py,sha256=nXlmtbfyy4VrbZ8PRFzUW49R6DukTRZIxeeph_9k6UY,1827
|
|
2
|
-
defistream/client.py,sha256=Ku8ouDbM6Mx4lVmqvBwNvNt-h2FkvqauPMSjKyPkjU4,12717
|
|
3
|
-
defistream/exceptions.py,sha256=_GxZQ18_YvXFtmNHeddWV8fHPIllHgFeP7fP0CmHF1k,1492
|
|
4
|
-
defistream/models.py,sha256=JiG4IUq19HIANz9MHQCkaE6onGNyNzXcbdJmosrHsw0,3812
|
|
5
|
-
defistream/protocols.py,sha256=b9zX0RUaw9mwZA-rPCPfpRisN5X-mdwmXSoTPYS-ius,11811
|
|
6
|
-
defistream/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
defistream/query.py,sha256=73vu8uXBdiFqGKoU0nDHDQ8emLnt1HVJMmvrl--Hzmg,17791
|
|
8
|
-
defistream-1.0.0.dist-info/METADATA,sha256=m6_u2yhrLNNHnm8HthaDx0pYpGtENY-CO7bz-qQeh94,12079
|
|
9
|
-
defistream-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
10
|
-
defistream-1.0.0.dist-info/licenses/LICENSE,sha256=72DWAof8dMePfFQmfaswClW5d-sE6k7p-7VpuSKLmU4,1067
|
|
11
|
-
defistream-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|