defipipe 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- defipipe-0.1.0/.gitignore +29 -0
- defipipe-0.1.0/PKG-INFO +80 -0
- defipipe-0.1.0/README.md +62 -0
- defipipe-0.1.0/defipipe/__init__.py +6 -0
- defipipe-0.1.0/defipipe/client.py +146 -0
- defipipe-0.1.0/pyproject.toml +33 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# deps
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnpm-store/
|
|
4
|
+
|
|
5
|
+
# builds
|
|
6
|
+
.next/
|
|
7
|
+
dist/
|
|
8
|
+
out/
|
|
9
|
+
.turbo/
|
|
10
|
+
|
|
11
|
+
# ponder
|
|
12
|
+
apps/indexer/.ponder/
|
|
13
|
+
apps/indexer/generated/
|
|
14
|
+
|
|
15
|
+
# env
|
|
16
|
+
.env
|
|
17
|
+
.env.*
|
|
18
|
+
!.env.example
|
|
19
|
+
|
|
20
|
+
# misc
|
|
21
|
+
.DS_Store
|
|
22
|
+
*.log
|
|
23
|
+
|
|
24
|
+
# python
|
|
25
|
+
__pycache__/
|
|
26
|
+
*.egg-info/
|
|
27
|
+
.venv/
|
|
28
|
+
*.tsbuildinfo
|
|
29
|
+
.vercel
|
defipipe-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: defipipe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Per-block, point-in-time DeFi contract state as pandas DataFrames — Aave rates, Uniswap prices, Lido supply, aligned onto one time axis.
|
|
5
|
+
Project-URL: Homepage, https://defipipe.io
|
|
6
|
+
Project-URL: Documentation, https://defipipe.io/docs
|
|
7
|
+
Author: defipipe
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: aave,blockchain-data,defi,ethereum,historical-data,lido,pandas,quant,time-series,uniswap
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Requires-Dist: pandas>=1.5
|
|
16
|
+
Requires-Dist: requests>=2.28
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# defipipe — per-block DeFi state as pandas DataFrames
|
|
20
|
+
|
|
21
|
+
**What read functions actually returned at every Ethereum block** — Aave borrow
|
|
22
|
+
rates, Uniswap slot0 prices, Lido total staked ETH — reconstructed historically,
|
|
23
|
+
indexed live, and aligned onto one clean time axis.
|
|
24
|
+
|
|
25
|
+
Most DeFi data is built from *events*. defipipe serves *state*: the result of
|
|
26
|
+
calling a contract's read functions at a specific block. If you need to know
|
|
27
|
+
what the Aave WETH borrow rate actually was at block 19,000,000 — not an hourly
|
|
28
|
+
average from a dashboard — this is the API for it.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install defipipe
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Five lines to an aligned DataFrame
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from defipipe import Client
|
|
40
|
+
|
|
41
|
+
df = Client().aligned([
|
|
42
|
+
"eth:aave:v3:0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2/weth:call:getreservedata[currentvariableborrowrate]",
|
|
43
|
+
"eth:lido:v2:0xae7ab96520de3a18e5e111b5eaab095312d7fe84:call:gettotalpooledether",
|
|
44
|
+
], freq="1h", since="2026-01-01")
|
|
45
|
+
|
|
46
|
+
df.corr()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
No API key needed for recent data at hourly bars. Full history and per-block
|
|
50
|
+
granularity: get a key at https://defipipe.io/pricing and set
|
|
51
|
+
`DEFIPIPE_API_KEY` (or pass `api_key=`).
|
|
52
|
+
|
|
53
|
+
## Why quants use this
|
|
54
|
+
|
|
55
|
+
- **Point-in-time correctness.** Every value is the last observation **at or
|
|
56
|
+
before** each interval boundary, resolved through the block↔timestamp map.
|
|
57
|
+
No interpolation, no lookahead bias — safe for backtests.
|
|
58
|
+
- **Exact raw values.** `client.series(id)` returns `value_raw` as the exact
|
|
59
|
+
on-chain string (uint256-safe) alongside the unit-scaled float.
|
|
60
|
+
- **One key for everything.** Canonical series IDs
|
|
61
|
+
(`{chain}:{protocol}:{version}:{instance}[/{scope}]:{kind}:{metric}`) name
|
|
62
|
+
every series across protocols — browse them at https://defipipe.io/datasets.
|
|
63
|
+
- **Per-block granularity** back to each pool's deployment, from our own
|
|
64
|
+
archive node — not sampled snapshots.
|
|
65
|
+
|
|
66
|
+
## Raw per-block rows
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
raw = Client(api_key="dp_...").series(
|
|
70
|
+
"eth:uniswap:v3:0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640:call:slot0[sqrtpricex96]",
|
|
71
|
+
since="2026-06-01",
|
|
72
|
+
)
|
|
73
|
+
raw.head() # block_number → ts, value_raw, value
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Coverage
|
|
77
|
+
|
|
78
|
+
Aave V3, Uniswap V3, Lido today; Morpho, GMX V2, Uniswap V4, Ethena, Maple
|
|
79
|
+
next. Request a pool at https://defipipe.io/feature-requests — approved
|
|
80
|
+
requests ship with full backfilled history.
|
defipipe-0.1.0/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# defipipe — per-block DeFi state as pandas DataFrames
|
|
2
|
+
|
|
3
|
+
**What read functions actually returned at every Ethereum block** — Aave borrow
|
|
4
|
+
rates, Uniswap slot0 prices, Lido total staked ETH — reconstructed historically,
|
|
5
|
+
indexed live, and aligned onto one clean time axis.
|
|
6
|
+
|
|
7
|
+
Most DeFi data is built from *events*. defipipe serves *state*: the result of
|
|
8
|
+
calling a contract's read functions at a specific block. If you need to know
|
|
9
|
+
what the Aave WETH borrow rate actually was at block 19,000,000 — not an hourly
|
|
10
|
+
average from a dashboard — this is the API for it.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install defipipe
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Five lines to an aligned DataFrame
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from defipipe import Client
|
|
22
|
+
|
|
23
|
+
df = Client().aligned([
|
|
24
|
+
"eth:aave:v3:0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2/weth:call:getreservedata[currentvariableborrowrate]",
|
|
25
|
+
"eth:lido:v2:0xae7ab96520de3a18e5e111b5eaab095312d7fe84:call:gettotalpooledether",
|
|
26
|
+
], freq="1h", since="2026-01-01")
|
|
27
|
+
|
|
28
|
+
df.corr()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
No API key needed for recent data at hourly bars. Full history and per-block
|
|
32
|
+
granularity: get a key at https://defipipe.io/pricing and set
|
|
33
|
+
`DEFIPIPE_API_KEY` (or pass `api_key=`).
|
|
34
|
+
|
|
35
|
+
## Why quants use this
|
|
36
|
+
|
|
37
|
+
- **Point-in-time correctness.** Every value is the last observation **at or
|
|
38
|
+
before** each interval boundary, resolved through the block↔timestamp map.
|
|
39
|
+
No interpolation, no lookahead bias — safe for backtests.
|
|
40
|
+
- **Exact raw values.** `client.series(id)` returns `value_raw` as the exact
|
|
41
|
+
on-chain string (uint256-safe) alongside the unit-scaled float.
|
|
42
|
+
- **One key for everything.** Canonical series IDs
|
|
43
|
+
(`{chain}:{protocol}:{version}:{instance}[/{scope}]:{kind}:{metric}`) name
|
|
44
|
+
every series across protocols — browse them at https://defipipe.io/datasets.
|
|
45
|
+
- **Per-block granularity** back to each pool's deployment, from our own
|
|
46
|
+
archive node — not sampled snapshots.
|
|
47
|
+
|
|
48
|
+
## Raw per-block rows
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
raw = Client(api_key="dp_...").series(
|
|
52
|
+
"eth:uniswap:v3:0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640:call:slot0[sqrtpricex96]",
|
|
53
|
+
since="2026-06-01",
|
|
54
|
+
)
|
|
55
|
+
raw.head() # block_number → ts, value_raw, value
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Coverage
|
|
59
|
+
|
|
60
|
+
Aave V3, Uniswap V3, Lido today; Morpho, GMX V2, Uniswap V4, Ethena, Maple
|
|
61
|
+
next. Request a pool at https://defipipe.io/feature-requests — approved
|
|
62
|
+
requests ship with full backfilled history.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from typing import Iterable, Optional, Union
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import requests
|
|
9
|
+
from requests.adapters import HTTPAdapter
|
|
10
|
+
from urllib3.util.retry import Retry
|
|
11
|
+
|
|
12
|
+
Timestampish = Union[str, datetime, None]
|
|
13
|
+
|
|
14
|
+
DEFAULT_BASE_URL = "https://api.defipipe.io"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DefipipeError(RuntimeError):
|
|
18
|
+
"""Raised when the API returns an error response."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, status: int, message: str):
|
|
21
|
+
self.status = status
|
|
22
|
+
super().__init__(f"[{status}] {message}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _iso(t: Timestampish) -> Optional[str]:
|
|
26
|
+
if t is None:
|
|
27
|
+
return None
|
|
28
|
+
if isinstance(t, datetime):
|
|
29
|
+
if t.tzinfo is None:
|
|
30
|
+
t = t.replace(tzinfo=timezone.utc)
|
|
31
|
+
return t.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
32
|
+
return t # assume ISO-8601 string (or anything Date.parse accepts)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Client:
|
|
36
|
+
"""defipipe API client.
|
|
37
|
+
|
|
38
|
+
>>> from defipipe import Client
|
|
39
|
+
>>> df = Client().aligned([
|
|
40
|
+
... "eth:aave:v3:0x8787.../weth:call:getreservedata[currentvariableborrowrate]",
|
|
41
|
+
... "eth:uniswap:v3:0x88e6...:call:slot0[sqrtpricex96]",
|
|
42
|
+
... ], freq="1h", since="2026-01-01")
|
|
43
|
+
|
|
44
|
+
No API key is needed for recent data at coarse frequencies (free tier).
|
|
45
|
+
Set ``DEFIPIPE_API_KEY`` (or pass ``api_key=``) for full history and
|
|
46
|
+
per-block granularity.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
api_key: Optional[str] = None,
|
|
52
|
+
base_url: Optional[str] = None,
|
|
53
|
+
timeout: float = 30.0,
|
|
54
|
+
max_retries: int = 3,
|
|
55
|
+
):
|
|
56
|
+
self.api_key = api_key or os.environ.get("DEFIPIPE_API_KEY")
|
|
57
|
+
self.base_url = (
|
|
58
|
+
base_url or os.environ.get("DEFIPIPE_API_URL", DEFAULT_BASE_URL)
|
|
59
|
+
).rstrip("/")
|
|
60
|
+
self.timeout = timeout
|
|
61
|
+
self._http = requests.Session()
|
|
62
|
+
retry = Retry(
|
|
63
|
+
total=max_retries,
|
|
64
|
+
backoff_factor=0.5,
|
|
65
|
+
status_forcelist=[429, 500, 502, 503, 504],
|
|
66
|
+
allowed_methods=["GET"],
|
|
67
|
+
)
|
|
68
|
+
self._http.mount("http://", HTTPAdapter(max_retries=retry))
|
|
69
|
+
self._http.mount("https://", HTTPAdapter(max_retries=retry))
|
|
70
|
+
|
|
71
|
+
def _get(self, path: str, params: dict) -> dict:
|
|
72
|
+
headers = {}
|
|
73
|
+
if self.api_key:
|
|
74
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
75
|
+
r = self._http.get(
|
|
76
|
+
f"{self.base_url}{path}",
|
|
77
|
+
params={k: v for k, v in params.items() if v is not None},
|
|
78
|
+
headers=headers,
|
|
79
|
+
timeout=self.timeout,
|
|
80
|
+
)
|
|
81
|
+
if r.status_code >= 400:
|
|
82
|
+
try:
|
|
83
|
+
msg = r.json().get("error", r.text)
|
|
84
|
+
except ValueError:
|
|
85
|
+
msg = r.text
|
|
86
|
+
raise DefipipeError(r.status_code, msg)
|
|
87
|
+
return r.json()
|
|
88
|
+
|
|
89
|
+
def aligned(
|
|
90
|
+
self,
|
|
91
|
+
series: Iterable[str],
|
|
92
|
+
freq: str = "1h",
|
|
93
|
+
since: Timestampish = None,
|
|
94
|
+
until: Timestampish = None,
|
|
95
|
+
) -> pd.DataFrame:
|
|
96
|
+
"""Heterogeneous series resampled onto one timestamp axis.
|
|
97
|
+
|
|
98
|
+
Semantics: for each interval boundary, the **last value at or before**
|
|
99
|
+
that boundary (resolved server-side through the block↔time map) — no
|
|
100
|
+
interpolation, no lookahead.
|
|
101
|
+
"""
|
|
102
|
+
ids = list(series)
|
|
103
|
+
data = self._get(
|
|
104
|
+
"/v1/aligned",
|
|
105
|
+
{
|
|
106
|
+
"series": ",".join(ids),
|
|
107
|
+
"freq": freq,
|
|
108
|
+
"from": _iso(since),
|
|
109
|
+
"to": _iso(until),
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
idx = pd.DatetimeIndex(pd.to_datetime(data["index"], utc=True), name="ts")
|
|
113
|
+
return pd.DataFrame({i: data["series"][i] for i in ids}, index=idx)
|
|
114
|
+
|
|
115
|
+
def series(
|
|
116
|
+
self,
|
|
117
|
+
series_id: str,
|
|
118
|
+
since: Timestampish = None,
|
|
119
|
+
until: Timestampish = None,
|
|
120
|
+
limit: int = 1000,
|
|
121
|
+
) -> pd.DataFrame:
|
|
122
|
+
"""Raw per-block rows for one canonical series (paginates transparently).
|
|
123
|
+
|
|
124
|
+
Columns: ``block_number``, ``ts``, ``value_raw`` (exact on-chain value
|
|
125
|
+
as a string — uint256-safe), ``value`` (unit-scaled float).
|
|
126
|
+
"""
|
|
127
|
+
rows: list = []
|
|
128
|
+
after: Optional[int] = None
|
|
129
|
+
while True:
|
|
130
|
+
data = self._get(
|
|
131
|
+
f"/v1/series/{requests.utils.quote(series_id, safe='')}",
|
|
132
|
+
{
|
|
133
|
+
"from": _iso(since),
|
|
134
|
+
"to": _iso(until),
|
|
135
|
+
"limit": limit,
|
|
136
|
+
"after_block": after,
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
rows.extend(data["rows"])
|
|
140
|
+
after = data.get("next_after_block")
|
|
141
|
+
if after is None:
|
|
142
|
+
break
|
|
143
|
+
df = pd.DataFrame(rows, columns=["block_number", "ts", "value_raw", "value"])
|
|
144
|
+
if len(df):
|
|
145
|
+
df["ts"] = pd.to_datetime(df["ts"], unit="s", utc=True)
|
|
146
|
+
return df.set_index("block_number")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "defipipe"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Per-block, point-in-time DeFi contract state as pandas DataFrames — Aave rates, Uniswap prices, Lido supply, aligned onto one time axis."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{ name = "defipipe" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"defi", "ethereum", "blockchain-data", "pandas", "quant",
|
|
15
|
+
"aave", "uniswap", "lido", "historical-data", "time-series",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"pandas>=1.5",
|
|
25
|
+
"requests>=2.28",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://defipipe.io"
|
|
30
|
+
Documentation = "https://defipipe.io/docs"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["defipipe"]
|