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.
@@ -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
@@ -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.
@@ -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,6 @@
1
+ """defipipe — per-block DeFi contract state as pandas DataFrames."""
2
+
3
+ from .client import Client, DefipipeError
4
+
5
+ __version__ = "0.1.0"
6
+ __all__ = ["Client", "DefipipeError"]
@@ -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"]