defillama-sdk 0.1.0__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.
- defillama_api/__init__.py +71 -0
- defillama_api/client.py +157 -0
- defillama_api/constants/__init__.py +11 -0
- defillama_api/constants/dimensions.py +69 -0
- defillama_api/errors.py +41 -0
- defillama_api/modules/__init__.py +29 -0
- defillama_api/modules/account.py +29 -0
- defillama_api/modules/bridges.py +81 -0
- defillama_api/modules/dat.py +31 -0
- defillama_api/modules/ecosystem.py +54 -0
- defillama_api/modules/emissions.py +32 -0
- defillama_api/modules/etfs.py +58 -0
- defillama_api/modules/fees.py +242 -0
- defillama_api/modules/prices.py +122 -0
- defillama_api/modules/stablecoins.py +76 -0
- defillama_api/modules/tvl.py +84 -0
- defillama_api/modules/volumes.py +116 -0
- defillama_api/modules/yields.py +85 -0
- defillama_api/py.typed +0 -0
- defillama_api/types/__init__.py +14 -0
- defillama_api/types/account.py +10 -0
- defillama_api/types/bridges.py +147 -0
- defillama_api/types/dat.py +144 -0
- defillama_api/types/ecosystem.py +175 -0
- defillama_api/types/emissions.py +154 -0
- defillama_api/types/etfs.py +37 -0
- defillama_api/types/fees.py +217 -0
- defillama_api/types/prices.py +99 -0
- defillama_api/types/stablecoins.py +159 -0
- defillama_api/types/tvl.py +189 -0
- defillama_api/types/volumes.py +147 -0
- defillama_api/types/yields.py +170 -0
- defillama_sdk-0.1.0.dist-info/METADATA +688 -0
- defillama_sdk-0.1.0.dist-info/RECORD +36 -0
- defillama_sdk-0.1.0.dist-info/WHEEL +5 -0
- defillama_sdk-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""DefiLlama Python SDK entry point."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from .client import BaseClient, DefiLlamaConfig
|
|
6
|
+
from .errors import ApiError, ApiKeyRequiredError, DefiLlamaError, NotFoundError, RateLimitError
|
|
7
|
+
from .modules.account import AccountModule
|
|
8
|
+
from .modules.bridges import BridgesModule
|
|
9
|
+
from .modules.dat import DatModule
|
|
10
|
+
from .modules.ecosystem import EcosystemModule
|
|
11
|
+
from .modules.emissions import EmissionsModule
|
|
12
|
+
from .modules.etfs import EtfsModule
|
|
13
|
+
from .modules.fees import FeesModule
|
|
14
|
+
from .modules.prices import PricesModule
|
|
15
|
+
from .modules.stablecoins import StablecoinsModule
|
|
16
|
+
from .modules.tvl import TvlModule
|
|
17
|
+
from .modules.volumes import VolumesModule
|
|
18
|
+
from .modules.yields import YieldsModule
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DefiLlama:
|
|
22
|
+
"""DefiLlama API client for accessing DeFi data."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, config: Optional[DefiLlamaConfig] = None) -> None:
|
|
25
|
+
"""Create a new DefiLlama client."""
|
|
26
|
+
|
|
27
|
+
self._client = BaseClient(config)
|
|
28
|
+
self.tvl = TvlModule(self._client)
|
|
29
|
+
self.prices = PricesModule(self._client)
|
|
30
|
+
self.stablecoins = StablecoinsModule(self._client)
|
|
31
|
+
self.yields = YieldsModule(self._client)
|
|
32
|
+
self.volumes = VolumesModule(self._client)
|
|
33
|
+
self.fees = FeesModule(self._client)
|
|
34
|
+
self.emissions = EmissionsModule(self._client)
|
|
35
|
+
self.bridges = BridgesModule(self._client)
|
|
36
|
+
self.ecosystem = EcosystemModule(self._client)
|
|
37
|
+
self.etfs = EtfsModule(self._client)
|
|
38
|
+
self.dat = DatModule(self._client)
|
|
39
|
+
self.account = AccountModule(self._client)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def isPro(self) -> bool:
|
|
43
|
+
"""Return True when the client has a Pro API key configured."""
|
|
44
|
+
|
|
45
|
+
return self._client.has_api_key
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"DefiLlama",
|
|
50
|
+
"DefiLlamaConfig",
|
|
51
|
+
"ApiError",
|
|
52
|
+
"ApiKeyRequiredError",
|
|
53
|
+
"DefiLlamaError",
|
|
54
|
+
"NotFoundError",
|
|
55
|
+
"RateLimitError",
|
|
56
|
+
"TvlModule",
|
|
57
|
+
"PricesModule",
|
|
58
|
+
"StablecoinsModule",
|
|
59
|
+
"YieldsModule",
|
|
60
|
+
"VolumesModule",
|
|
61
|
+
"FeesModule",
|
|
62
|
+
"EmissionsModule",
|
|
63
|
+
"BridgesModule",
|
|
64
|
+
"EcosystemModule",
|
|
65
|
+
"EtfsModule",
|
|
66
|
+
"DatModule",
|
|
67
|
+
"AccountModule",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
from .constants import *
|
|
71
|
+
from .types import *
|
defillama_api/client.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""HTTP client for the DefiLlama API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Mapping, Optional, Literal, TypedDict
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from .errors import ApiError, ApiKeyRequiredError, NotFoundError, RateLimitError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DefiLlamaConfig(TypedDict, total=False):
|
|
13
|
+
api_key: str
|
|
14
|
+
apiKey: str
|
|
15
|
+
timeout: int
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
BaseUrl = Literal["main", "v2", "bridges", "coins", "stablecoins"]
|
|
19
|
+
|
|
20
|
+
BASE_URLS: Dict[str, str] = {
|
|
21
|
+
"main": "https://api.llama.fi",
|
|
22
|
+
"v2": "https://api.llama.fi/v2",
|
|
23
|
+
"pro": "https://pro-api.llama.fi",
|
|
24
|
+
"bridges": "https://bridges.llama.fi",
|
|
25
|
+
"coins": "https://coins.llama.fi",
|
|
26
|
+
"stablecoins": "https://stablecoins.llama.fi",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class BaseClient:
|
|
31
|
+
"""Low-level HTTP client used by all modules."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, config: Optional[Mapping[str, Any]] = None) -> None:
|
|
34
|
+
config = config or {}
|
|
35
|
+
api_key = None
|
|
36
|
+
if "api_key" in config:
|
|
37
|
+
api_key = config.get("api_key")
|
|
38
|
+
elif "apiKey" in config:
|
|
39
|
+
api_key = config.get("apiKey")
|
|
40
|
+
self._api_key = api_key
|
|
41
|
+
timeout_ms = config.get("timeout", 30000)
|
|
42
|
+
self._timeout = timeout_ms / 1000.0
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def has_api_key(self) -> bool:
|
|
46
|
+
"""Return True when a Pro API key is configured."""
|
|
47
|
+
|
|
48
|
+
return bool(self._api_key)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def timeout(self) -> float:
|
|
52
|
+
"""Return the configured request timeout in seconds."""
|
|
53
|
+
|
|
54
|
+
return self._timeout
|
|
55
|
+
|
|
56
|
+
def get_api_key(self) -> Optional[str]:
|
|
57
|
+
"""Return the configured API key, if any."""
|
|
58
|
+
|
|
59
|
+
return self._api_key
|
|
60
|
+
|
|
61
|
+
def _build_url(
|
|
62
|
+
self,
|
|
63
|
+
endpoint: str,
|
|
64
|
+
requires_auth: bool,
|
|
65
|
+
base: BaseUrl = "main",
|
|
66
|
+
api_namespace: str = "api",
|
|
67
|
+
) -> str:
|
|
68
|
+
if base == "v2":
|
|
69
|
+
if not self._api_key:
|
|
70
|
+
raise ApiKeyRequiredError(endpoint)
|
|
71
|
+
return f"{BASE_URLS['pro']}/{self._api_key}/api/v2{endpoint}"
|
|
72
|
+
if base == "bridges":
|
|
73
|
+
if not self._api_key:
|
|
74
|
+
raise ApiKeyRequiredError(endpoint)
|
|
75
|
+
return f"{BASE_URLS['pro']}/{self._api_key}/bridges{endpoint}"
|
|
76
|
+
if base == "coins":
|
|
77
|
+
return f"{BASE_URLS['coins']}{endpoint}"
|
|
78
|
+
if base == "stablecoins":
|
|
79
|
+
return f"{BASE_URLS['stablecoins']}{endpoint}"
|
|
80
|
+
if requires_auth:
|
|
81
|
+
if not self._api_key:
|
|
82
|
+
raise ApiKeyRequiredError(endpoint)
|
|
83
|
+
namespace = f"/{api_namespace}" if api_namespace else ""
|
|
84
|
+
return f"{BASE_URLS['pro']}/{self._api_key}{namespace}{endpoint}"
|
|
85
|
+
return f"{BASE_URLS['main']}{endpoint}"
|
|
86
|
+
|
|
87
|
+
def _handle_response(self, response: requests.Response, endpoint: str) -> Any:
|
|
88
|
+
if response.ok:
|
|
89
|
+
return response.json()
|
|
90
|
+
text = response.text
|
|
91
|
+
if response.status_code == 404:
|
|
92
|
+
raise NotFoundError(endpoint)
|
|
93
|
+
if response.status_code == 429:
|
|
94
|
+
retry_after = response.headers.get("Retry-After")
|
|
95
|
+
retry_value = int(retry_after) if retry_after and retry_after.isdigit() else None
|
|
96
|
+
raise RateLimitError(retry_value)
|
|
97
|
+
try:
|
|
98
|
+
error_body = response.json()
|
|
99
|
+
except ValueError:
|
|
100
|
+
error_body = text
|
|
101
|
+
raise ApiError(
|
|
102
|
+
response.status_code,
|
|
103
|
+
f"API request failed: {response.reason}",
|
|
104
|
+
error_body,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def _filter_params(self, params: Optional[Mapping[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
108
|
+
if not params:
|
|
109
|
+
return None
|
|
110
|
+
filtered: Dict[str, Any] = {}
|
|
111
|
+
for key, value in params.items():
|
|
112
|
+
if value is not None:
|
|
113
|
+
filtered[key] = value
|
|
114
|
+
return filtered or None
|
|
115
|
+
|
|
116
|
+
def get(
|
|
117
|
+
self,
|
|
118
|
+
endpoint: str,
|
|
119
|
+
*,
|
|
120
|
+
params: Optional[Mapping[str, Any]] = None,
|
|
121
|
+
requires_auth: bool = False,
|
|
122
|
+
base: BaseUrl = "main",
|
|
123
|
+
api_namespace: str = "api",
|
|
124
|
+
) -> Any:
|
|
125
|
+
"""Perform a GET request and return parsed JSON."""
|
|
126
|
+
|
|
127
|
+
url = self._build_url(endpoint, requires_auth, base, api_namespace)
|
|
128
|
+
response = requests.get(
|
|
129
|
+
url,
|
|
130
|
+
params=self._filter_params(params),
|
|
131
|
+
timeout=self._timeout,
|
|
132
|
+
headers={"Accept": "application/json"},
|
|
133
|
+
)
|
|
134
|
+
return self._handle_response(response, endpoint)
|
|
135
|
+
|
|
136
|
+
def post(
|
|
137
|
+
self,
|
|
138
|
+
endpoint: str,
|
|
139
|
+
body: Any,
|
|
140
|
+
*,
|
|
141
|
+
requires_auth: bool = False,
|
|
142
|
+
base: BaseUrl = "main",
|
|
143
|
+
api_namespace: str = "api",
|
|
144
|
+
) -> Any:
|
|
145
|
+
"""Perform a POST request and return parsed JSON."""
|
|
146
|
+
|
|
147
|
+
url = self._build_url(endpoint, requires_auth, base, api_namespace)
|
|
148
|
+
response = requests.post(
|
|
149
|
+
url,
|
|
150
|
+
json=body,
|
|
151
|
+
timeout=self._timeout,
|
|
152
|
+
headers={
|
|
153
|
+
"Accept": "application/json",
|
|
154
|
+
"Content-Type": "application/json",
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
return self._handle_response(response, endpoint)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Constants for DefiLlama API dimensions."""
|
|
2
|
+
|
|
3
|
+
from .dimensions import AdapterType, FeeDataType, VolumeDataType, DataTypeShortKeys, DataTypeShortKey
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"AdapterType",
|
|
7
|
+
"FeeDataType",
|
|
8
|
+
"VolumeDataType",
|
|
9
|
+
"DataTypeShortKeys",
|
|
10
|
+
"DataTypeShortKey",
|
|
11
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Dimension constants used by fees and volume endpoints."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Dict, Literal
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AdapterType(str, Enum):
|
|
8
|
+
DEXS = "dexs"
|
|
9
|
+
FEES = "fees"
|
|
10
|
+
AGGREGATORS = "aggregators"
|
|
11
|
+
DERIVATIVES = "derivatives"
|
|
12
|
+
AGGREGATOR_DERIVATIVES = "aggregator-derivatives"
|
|
13
|
+
OPTIONS = "options"
|
|
14
|
+
BRIDGE_AGGREGATORS = "bridge-aggregators"
|
|
15
|
+
OPEN_INTEREST = "open-interest"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FeeDataType(str, Enum):
|
|
19
|
+
DAILY_FEES = "dailyFees"
|
|
20
|
+
DAILY_REVENUE = "dailyRevenue"
|
|
21
|
+
DAILY_HOLDERS_REVENUE = "dailyHoldersRevenue"
|
|
22
|
+
DAILY_SUPPLY_SIDE_REVENUE = "dailySupplySideRevenue"
|
|
23
|
+
DAILY_BRIBES_REVENUE = "dailyBribesRevenue"
|
|
24
|
+
DAILY_TOKEN_TAXES = "dailyTokenTaxes"
|
|
25
|
+
DAILY_APP_FEES = "dailyAppFees"
|
|
26
|
+
DAILY_APP_REVENUE = "dailyAppRevenue"
|
|
27
|
+
DAILY_EARNINGS = "dailyEarnings"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class VolumeDataType(str, Enum):
|
|
31
|
+
DAILY_VOLUME = "dailyVolume"
|
|
32
|
+
TOTAL_VOLUME = "totalVolume"
|
|
33
|
+
DAILY_NOTIONAL_VOLUME = "dailyNotionalVolume"
|
|
34
|
+
DAILY_PREMIUM_VOLUME = "dailyPremiumVolume"
|
|
35
|
+
DAILY_BRIDGE_VOLUME = "dailyBridgeVolume"
|
|
36
|
+
OPEN_INTEREST_AT_END = "openInterestAtEnd"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
DataTypeShortKeys: Dict[str, str] = {
|
|
40
|
+
"dailyFees": "df",
|
|
41
|
+
"dailyRevenue": "dr",
|
|
42
|
+
"dailyHoldersRevenue": "dhr",
|
|
43
|
+
"dailySupplySideRevenue": "dssr",
|
|
44
|
+
"dailyBribesRevenue": "dbr",
|
|
45
|
+
"dailyTokenTaxes": "dtt",
|
|
46
|
+
"dailyAppRevenue": "dar",
|
|
47
|
+
"dailyAppFees": "daf",
|
|
48
|
+
"dailyNotionalVolume": "dnv",
|
|
49
|
+
"dailyPremiumVolume": "dpv",
|
|
50
|
+
"openInterestAtEnd": "doi",
|
|
51
|
+
"dailyVolume": "dv",
|
|
52
|
+
"dailyBridgeVolume": "dbv",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
DataTypeShortKey = Literal[
|
|
56
|
+
"dailyFees",
|
|
57
|
+
"dailyRevenue",
|
|
58
|
+
"dailyHoldersRevenue",
|
|
59
|
+
"dailySupplySideRevenue",
|
|
60
|
+
"dailyBribesRevenue",
|
|
61
|
+
"dailyTokenTaxes",
|
|
62
|
+
"dailyAppRevenue",
|
|
63
|
+
"dailyAppFees",
|
|
64
|
+
"dailyNotionalVolume",
|
|
65
|
+
"dailyPremiumVolume",
|
|
66
|
+
"openInterestAtEnd",
|
|
67
|
+
"dailyVolume",
|
|
68
|
+
"dailyBridgeVolume",
|
|
69
|
+
]
|
defillama_api/errors.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Error types for the DefiLlama SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DefiLlamaError(Exception):
|
|
7
|
+
"""Base error class for DefiLlama SDK errors."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str) -> None:
|
|
10
|
+
super().__init__(message)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApiKeyRequiredError(DefiLlamaError):
|
|
14
|
+
"""Raised when a Pro API key is required for an endpoint."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, endpoint: str) -> None:
|
|
17
|
+
super().__init__(f"API key required for endpoint: {endpoint}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RateLimitError(DefiLlamaError):
|
|
21
|
+
"""Raised when the API rate limit is exceeded."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, retry_after: Optional[int] = None) -> None:
|
|
24
|
+
super().__init__("Rate limit exceeded")
|
|
25
|
+
self.retry_after = retry_after
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class NotFoundError(DefiLlamaError):
|
|
29
|
+
"""Raised when a requested resource is not found."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, resource: str) -> None:
|
|
32
|
+
super().__init__(f"Resource not found: {resource}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ApiError(DefiLlamaError):
|
|
36
|
+
"""Raised for non-success API responses."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, status_code: int, message: str, response: Optional[object] = None) -> None:
|
|
39
|
+
super().__init__(message)
|
|
40
|
+
self.status_code = status_code
|
|
41
|
+
self.response = response
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Module exports for the DefiLlama SDK."""
|
|
2
|
+
|
|
3
|
+
from .account import AccountModule
|
|
4
|
+
from .bridges import BridgesModule
|
|
5
|
+
from .dat import DatModule
|
|
6
|
+
from .ecosystem import EcosystemModule
|
|
7
|
+
from .emissions import EmissionsModule
|
|
8
|
+
from .etfs import EtfsModule
|
|
9
|
+
from .fees import FeesModule
|
|
10
|
+
from .prices import PricesModule
|
|
11
|
+
from .stablecoins import StablecoinsModule
|
|
12
|
+
from .tvl import TvlModule
|
|
13
|
+
from .volumes import VolumesModule
|
|
14
|
+
from .yields import YieldsModule
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AccountModule",
|
|
18
|
+
"BridgesModule",
|
|
19
|
+
"DatModule",
|
|
20
|
+
"EcosystemModule",
|
|
21
|
+
"EmissionsModule",
|
|
22
|
+
"EtfsModule",
|
|
23
|
+
"FeesModule",
|
|
24
|
+
"PricesModule",
|
|
25
|
+
"StablecoinsModule",
|
|
26
|
+
"TvlModule",
|
|
27
|
+
"VolumesModule",
|
|
28
|
+
"YieldsModule",
|
|
29
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Account module implementation."""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from ..client import BaseClient
|
|
6
|
+
from ..errors import ApiKeyRequiredError, ApiError
|
|
7
|
+
from ..types.account import UsageResponse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AccountModule:
|
|
11
|
+
"""Access API account and usage data."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, client: BaseClient) -> None:
|
|
14
|
+
self._client = client
|
|
15
|
+
|
|
16
|
+
def getUsage(self) -> UsageResponse:
|
|
17
|
+
"""Get API usage statistics for the configured key."""
|
|
18
|
+
|
|
19
|
+
api_key = self._client.get_api_key()
|
|
20
|
+
if not api_key:
|
|
21
|
+
raise ApiKeyRequiredError("/usage")
|
|
22
|
+
response = requests.get(
|
|
23
|
+
f"https://pro-api.llama.fi/usage/{api_key}",
|
|
24
|
+
headers={"Accept": "application/json"},
|
|
25
|
+
timeout=self._client.timeout,
|
|
26
|
+
)
|
|
27
|
+
if not response.ok:
|
|
28
|
+
raise ApiError(response.status_code, f"Failed to fetch usage: {response.reason}")
|
|
29
|
+
return response.json()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Bridges module implementation."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
from ..client import BaseClient
|
|
7
|
+
from ..types.bridges import (
|
|
8
|
+
BridgeDayStatsResponse,
|
|
9
|
+
BridgeDetail,
|
|
10
|
+
BridgeTransaction,
|
|
11
|
+
BridgeTransactionsOptions,
|
|
12
|
+
BridgeVolumeDataPoint,
|
|
13
|
+
BridgesOptions,
|
|
14
|
+
BridgesResponse,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BridgesModule:
|
|
19
|
+
"""Access bridge volume and transaction data."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, client: BaseClient) -> None:
|
|
22
|
+
self._client = client
|
|
23
|
+
|
|
24
|
+
def getAll(self, options: Optional[BridgesOptions] = None) -> BridgesResponse:
|
|
25
|
+
"""Get all bridges with volume data."""
|
|
26
|
+
|
|
27
|
+
params = None
|
|
28
|
+
if options and options.get("includeChains") is not None:
|
|
29
|
+
params = {"includeChains": options.get("includeChains")}
|
|
30
|
+
return self._client.get(
|
|
31
|
+
"/bridges",
|
|
32
|
+
base="bridges",
|
|
33
|
+
params=params,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def getById(self, bridge_id: int) -> BridgeDetail:
|
|
37
|
+
"""Get detailed bridge information by ID."""
|
|
38
|
+
|
|
39
|
+
return self._client.get(
|
|
40
|
+
f"/bridge/{bridge_id}",
|
|
41
|
+
base="bridges",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def getVolumeByChain(self, chain: str) -> List[BridgeVolumeDataPoint]:
|
|
45
|
+
"""Get bridge volume for a chain."""
|
|
46
|
+
|
|
47
|
+
return self._client.get(
|
|
48
|
+
f"/bridgevolume/{quote(chain)}",
|
|
49
|
+
base="bridges",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def getDayStats(self, timestamp: int, chain: str) -> BridgeDayStatsResponse:
|
|
53
|
+
"""Get daily bridge statistics for a chain."""
|
|
54
|
+
|
|
55
|
+
return self._client.get(
|
|
56
|
+
f"/bridgedaystats/{timestamp}/{quote(chain)}",
|
|
57
|
+
base="bridges",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def getTransactions(
|
|
61
|
+
self, bridge_id: int, options: Optional[BridgeTransactionsOptions] = None
|
|
62
|
+
) -> List[BridgeTransaction]:
|
|
63
|
+
"""Get bridge transactions."""
|
|
64
|
+
|
|
65
|
+
params = {}
|
|
66
|
+
if options:
|
|
67
|
+
if options.get("limit") is not None:
|
|
68
|
+
params["limit"] = options.get("limit")
|
|
69
|
+
if options.get("startTimestamp") is not None:
|
|
70
|
+
params["startTimestamp"] = options.get("startTimestamp")
|
|
71
|
+
if options.get("endTimestamp") is not None:
|
|
72
|
+
params["endTimestamp"] = options.get("endTimestamp")
|
|
73
|
+
if options.get("sourceChain"):
|
|
74
|
+
params["sourceChain"] = options.get("sourceChain")
|
|
75
|
+
if options.get("address"):
|
|
76
|
+
params["address"] = options.get("address")
|
|
77
|
+
return self._client.get(
|
|
78
|
+
f"/transactions/{bridge_id}",
|
|
79
|
+
base="bridges",
|
|
80
|
+
params=params or None,
|
|
81
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""DAT module implementation."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from ..client import BaseClient
|
|
6
|
+
from ..types.dat import DatInstitutionResponse, DatInstitutionsResponse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DatModule:
|
|
10
|
+
"""Access Digital Asset Treasury data from DefiLlama."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, client: BaseClient) -> None:
|
|
13
|
+
self._client = client
|
|
14
|
+
|
|
15
|
+
def getInstitutions(self) -> DatInstitutionsResponse:
|
|
16
|
+
"""Get comprehensive DAT data for all institutions."""
|
|
17
|
+
|
|
18
|
+
return self._client.get(
|
|
19
|
+
"/dat/institutions",
|
|
20
|
+
requires_auth=True,
|
|
21
|
+
api_namespace="",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def getInstitution(self, symbol: str) -> DatInstitutionResponse:
|
|
25
|
+
"""Get detailed DAT data for a specific institution."""
|
|
26
|
+
|
|
27
|
+
return self._client.get(
|
|
28
|
+
f"/dat/institutions/{quote(symbol)}",
|
|
29
|
+
requires_auth=True,
|
|
30
|
+
api_namespace="",
|
|
31
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Ecosystem module implementation."""
|
|
2
|
+
|
|
3
|
+
from ..client import BaseClient
|
|
4
|
+
from ..types.ecosystem import (
|
|
5
|
+
CategoriesResponse,
|
|
6
|
+
Entity,
|
|
7
|
+
ForksResponse,
|
|
8
|
+
Hack,
|
|
9
|
+
OraclesResponse,
|
|
10
|
+
RaisesResponse,
|
|
11
|
+
Treasury,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EcosystemModule:
|
|
16
|
+
"""Access ecosystem-level data from DefiLlama."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: BaseClient) -> None:
|
|
19
|
+
self._client = client
|
|
20
|
+
|
|
21
|
+
def getCategories(self) -> CategoriesResponse:
|
|
22
|
+
"""Get TVL grouped by protocol category."""
|
|
23
|
+
|
|
24
|
+
return self._client.get("/categories", requires_auth=True)
|
|
25
|
+
|
|
26
|
+
def getForks(self) -> ForksResponse:
|
|
27
|
+
"""Get protocol fork relationships and TVL."""
|
|
28
|
+
|
|
29
|
+
return self._client.get("/forks", requires_auth=True)
|
|
30
|
+
|
|
31
|
+
def getOracles(self) -> OraclesResponse:
|
|
32
|
+
"""Get oracle usage data across protocols."""
|
|
33
|
+
|
|
34
|
+
return self._client.get("/oracles", requires_auth=True)
|
|
35
|
+
|
|
36
|
+
def getEntities(self) -> list[Entity]:
|
|
37
|
+
"""Get entity treasury and holdings data."""
|
|
38
|
+
|
|
39
|
+
return self._client.get("/entities", requires_auth=True)
|
|
40
|
+
|
|
41
|
+
def getTreasuries(self) -> list[Treasury]:
|
|
42
|
+
"""Get protocol treasury balances."""
|
|
43
|
+
|
|
44
|
+
return self._client.get("/treasuries", requires_auth=True)
|
|
45
|
+
|
|
46
|
+
def getHacks(self) -> list[Hack]:
|
|
47
|
+
"""Get security incidents and exploit data."""
|
|
48
|
+
|
|
49
|
+
return self._client.get("/hacks", requires_auth=True)
|
|
50
|
+
|
|
51
|
+
def getRaises(self) -> RaisesResponse:
|
|
52
|
+
"""Get funding rounds database."""
|
|
53
|
+
|
|
54
|
+
return self._client.get("/raises", requires_auth=True)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Emissions module implementation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
from ..client import BaseClient
|
|
7
|
+
from ..types.emissions import EmissionDetailResponse, EmissionToken
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EmissionsModule:
|
|
11
|
+
"""Access emission and unlock schedule data."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, client: BaseClient) -> None:
|
|
14
|
+
self._client = client
|
|
15
|
+
|
|
16
|
+
def getAll(self) -> list[EmissionToken]:
|
|
17
|
+
"""Get all tokens with unlock schedules."""
|
|
18
|
+
|
|
19
|
+
return self._client.get("/emissions", requires_auth=True)
|
|
20
|
+
|
|
21
|
+
def getByProtocol(self, protocol: str) -> EmissionDetailResponse:
|
|
22
|
+
"""Get detailed emission data for a protocol."""
|
|
23
|
+
|
|
24
|
+
response = self._client.get(
|
|
25
|
+
f"/emission/{quote(protocol)}",
|
|
26
|
+
requires_auth=True,
|
|
27
|
+
)
|
|
28
|
+
body = json.loads(response.get("body", "{}"))
|
|
29
|
+
return {
|
|
30
|
+
"body": body,
|
|
31
|
+
"lastModified": response.get("lastModified"),
|
|
32
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""ETFs module implementation."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from ..client import BaseClient
|
|
6
|
+
from ..types.etfs import EtfHistoryItem, EtfOverviewItem, FdvPeriod, FdvPerformanceItem
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EtfsModule:
|
|
10
|
+
"""Access ETF data from DefiLlama."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, client: BaseClient) -> None:
|
|
13
|
+
self._client = client
|
|
14
|
+
|
|
15
|
+
def getOverview(self) -> list[EtfOverviewItem]:
|
|
16
|
+
"""Get Bitcoin ETF overview."""
|
|
17
|
+
|
|
18
|
+
return self._client.get(
|
|
19
|
+
"/overview",
|
|
20
|
+
requires_auth=True,
|
|
21
|
+
api_namespace="etfs",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def getOverviewEth(self) -> list[EtfOverviewItem]:
|
|
25
|
+
"""Get Ethereum ETF overview."""
|
|
26
|
+
|
|
27
|
+
return self._client.get(
|
|
28
|
+
"/overviewEth",
|
|
29
|
+
requires_auth=True,
|
|
30
|
+
api_namespace="etfs",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def getHistory(self) -> list[EtfHistoryItem]:
|
|
34
|
+
"""Get Bitcoin ETF flow history."""
|
|
35
|
+
|
|
36
|
+
return self._client.get(
|
|
37
|
+
"/history",
|
|
38
|
+
requires_auth=True,
|
|
39
|
+
api_namespace="etfs",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def getHistoryEth(self) -> list[EtfHistoryItem]:
|
|
43
|
+
"""Get Ethereum ETF flow history."""
|
|
44
|
+
|
|
45
|
+
return self._client.get(
|
|
46
|
+
"/historyEth",
|
|
47
|
+
requires_auth=True,
|
|
48
|
+
api_namespace="etfs",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def getFdvPerformance(self, period: FdvPeriod) -> list[FdvPerformanceItem]:
|
|
52
|
+
"""Get FDV performance data."""
|
|
53
|
+
|
|
54
|
+
return self._client.get(
|
|
55
|
+
f"/performance/{quote(period)}",
|
|
56
|
+
requires_auth=True,
|
|
57
|
+
api_namespace="fdv",
|
|
58
|
+
)
|