wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.14__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.
Potentially problematic release.
This version of wayfinder-paths might be problematic. Click here for more details.
- wayfinder_paths/adapters/balance_adapter/README.md +13 -14
- wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
- wayfinder_paths/adapters/brap_adapter/README.md +11 -16
- wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
- wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
- wayfinder_paths/adapters/pool_adapter/README.md +9 -10
- wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
- wayfinder_paths/adapters/token_adapter/README.md +2 -14
- wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
- wayfinder_paths/adapters/token_adapter/examples.json +4 -8
- wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
- wayfinder_paths/core/clients/BRAPClient.py +102 -61
- wayfinder_paths/core/clients/ClientManager.py +1 -68
- wayfinder_paths/core/clients/HyperlendClient.py +125 -64
- wayfinder_paths/core/clients/LedgerClient.py +1 -4
- wayfinder_paths/core/clients/PoolClient.py +122 -48
- wayfinder_paths/core/clients/TokenClient.py +91 -36
- wayfinder_paths/core/clients/WalletClient.py +26 -56
- wayfinder_paths/core/clients/WayfinderClient.py +28 -160
- wayfinder_paths/core/clients/__init__.py +0 -2
- wayfinder_paths/core/clients/protocols.py +35 -46
- wayfinder_paths/core/clients/sdk_example.py +37 -22
- wayfinder_paths/core/engine/StrategyJob.py +7 -55
- wayfinder_paths/core/services/local_evm_txn.py +6 -6
- wayfinder_paths/core/services/local_token_txn.py +1 -1
- wayfinder_paths/core/strategies/Strategy.py +0 -2
- wayfinder_paths/core/utils/evm_helpers.py +2 -2
- wayfinder_paths/run_strategy.py +8 -19
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
- wayfinder_paths/templates/strategy/README.md +3 -3
- wayfinder_paths/templates/strategy/test_strategy.py +3 -2
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +14 -49
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +46 -47
- wayfinder_paths/core/clients/AuthClient.py +0 -83
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BRAP (Bridge/Router/Adapter Protocol) Client
|
|
3
|
-
Provides access to quote operations via the
|
|
3
|
+
Provides access to quote operations via the blockchain quote endpoint.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
@@ -10,100 +10,141 @@ from typing import Any, NotRequired, Required, TypedDict
|
|
|
10
10
|
|
|
11
11
|
from loguru import logger
|
|
12
12
|
|
|
13
|
-
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
14
13
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
15
14
|
from wayfinder_paths.core.config import get_api_base_url
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
class
|
|
17
|
+
class QuoteTx(TypedDict, total=False):
|
|
18
|
+
"""Quote transaction data structure"""
|
|
19
|
+
|
|
20
|
+
data: str
|
|
21
|
+
to: str
|
|
22
|
+
value: str
|
|
23
|
+
chainId: int
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class QuoteData(TypedDict):
|
|
27
|
+
"""Quote data structure"""
|
|
28
|
+
|
|
29
|
+
gas: Required[str]
|
|
30
|
+
amountOut: Required[str]
|
|
31
|
+
priceImpact: Required[int]
|
|
32
|
+
feeAmount: Required[list[str]]
|
|
33
|
+
minAmountOut: Required[str]
|
|
34
|
+
createdAt: Required[int]
|
|
35
|
+
tx: Required[QuoteTx]
|
|
36
|
+
route: Required[list[dict[str, Any]]]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class FeeBreakdown(TypedDict):
|
|
40
|
+
"""Fee breakdown structure"""
|
|
41
|
+
|
|
42
|
+
name: Required[str]
|
|
43
|
+
amount: Required[int]
|
|
44
|
+
amount_usd: Required[float]
|
|
45
|
+
token: Required[str]
|
|
46
|
+
token_chain: Required[int]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FeeEstimate(TypedDict):
|
|
50
|
+
"""Fee estimate structure"""
|
|
51
|
+
|
|
52
|
+
fee_total_usd: Required[float]
|
|
53
|
+
fee_breakdown: Required[list[FeeBreakdown]]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Calldata(TypedDict, total=False):
|
|
57
|
+
"""Calldata structure"""
|
|
58
|
+
|
|
59
|
+
data: str
|
|
60
|
+
to: str
|
|
61
|
+
value: str
|
|
62
|
+
chainId: int
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BRAPQuoteEntry(TypedDict):
|
|
66
|
+
"""BRAP quote entry structure"""
|
|
67
|
+
|
|
68
|
+
provider: Required[str]
|
|
69
|
+
quote: Required[QuoteData]
|
|
70
|
+
calldata: Required[Calldata]
|
|
71
|
+
output_amount: Required[int]
|
|
72
|
+
input_amount: Required[int]
|
|
73
|
+
gas_estimate: NotRequired[int | None]
|
|
74
|
+
error: NotRequired[str | None]
|
|
75
|
+
input_amount_usd: Required[float]
|
|
76
|
+
output_amount_usd: Required[float]
|
|
77
|
+
fee_estimate: Required[FeeEstimate]
|
|
78
|
+
wrap_transaction: NotRequired[dict[str, Any] | None]
|
|
79
|
+
unwrap_transaction: NotRequired[dict[str, Any] | None]
|
|
80
|
+
native_input: Required[bool]
|
|
81
|
+
native_output: Required[bool]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class BRAPQuoteResponse(TypedDict):
|
|
19
85
|
"""BRAP quote response structure"""
|
|
20
86
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
from_chain_id: Required[int]
|
|
24
|
-
to_chain_id: Required[int]
|
|
25
|
-
from_address: Required[str]
|
|
26
|
-
to_address: Required[str]
|
|
27
|
-
amount1: Required[str]
|
|
28
|
-
amount2: NotRequired[str]
|
|
29
|
-
routes: NotRequired[list[dict[str, Any]]]
|
|
30
|
-
best_route: NotRequired[dict[str, Any]]
|
|
31
|
-
fees: NotRequired[dict[str, Any] | None]
|
|
32
|
-
slippage: NotRequired[float | None]
|
|
33
|
-
wayfinder_fee: NotRequired[float | None]
|
|
87
|
+
quotes: Required[list[BRAPQuoteEntry]]
|
|
88
|
+
best_quote: Required[BRAPQuoteEntry]
|
|
34
89
|
|
|
35
90
|
|
|
36
91
|
class BRAPClient(WayfinderClient):
|
|
37
92
|
"""Client for BRAP quote operations"""
|
|
38
93
|
|
|
39
|
-
def __init__(self
|
|
40
|
-
super().__init__(
|
|
41
|
-
self.api_base_url = get_api_base_url()
|
|
42
|
-
self._auth_client: AuthClient | None = AuthClient(api_key=api_key)
|
|
94
|
+
def __init__(self):
|
|
95
|
+
super().__init__()
|
|
96
|
+
self.api_base_url = f"{get_api_base_url()}/v1/blockchain/braps"
|
|
43
97
|
|
|
44
98
|
async def get_quote(
|
|
45
99
|
self,
|
|
46
100
|
*,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
slippage: float | None = None,
|
|
55
|
-
wayfinder_fee: float | None = None,
|
|
56
|
-
) -> BRAPQuote:
|
|
101
|
+
from_token: str,
|
|
102
|
+
to_token: str,
|
|
103
|
+
from_chain: int,
|
|
104
|
+
to_chain: int,
|
|
105
|
+
from_wallet: str,
|
|
106
|
+
from_amount: str,
|
|
107
|
+
) -> BRAPQuoteResponse: # type: ignore # noqa: E501
|
|
57
108
|
"""
|
|
58
109
|
Get a quote for a bridge/swap operation.
|
|
59
110
|
|
|
60
111
|
Args:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
amount1: Amount to swap (in smallest units)
|
|
68
|
-
slippage: Maximum slippage tolerance (optional)
|
|
69
|
-
wayfinder_fee: Wayfinder fee (optional)
|
|
112
|
+
from_token: Source token contract address
|
|
113
|
+
to_token: Destination token contract address
|
|
114
|
+
from_chain: Source chain ID
|
|
115
|
+
to_chain: Destination chain ID
|
|
116
|
+
from_wallet: Source wallet address
|
|
117
|
+
from_amount: Amount to swap (in smallest units)
|
|
70
118
|
|
|
71
119
|
Returns:
|
|
72
|
-
Quote
|
|
120
|
+
Quote response including quotes array and best_quote
|
|
73
121
|
"""
|
|
74
122
|
logger.info(
|
|
75
|
-
f"Getting BRAP quote: {
|
|
76
|
-
)
|
|
77
|
-
logger.debug(
|
|
78
|
-
f"Quote params: amount={amount1}, slippage={slippage}, wayfinder_fee={wayfinder_fee}"
|
|
123
|
+
f"Getting BRAP quote: {from_token} -> {to_token} (chain {from_chain} -> {to_chain})"
|
|
79
124
|
)
|
|
125
|
+
logger.debug(f"Quote params: amount={from_amount}")
|
|
80
126
|
start_time = time.time()
|
|
81
127
|
|
|
82
|
-
url = f"{self.api_base_url}/
|
|
128
|
+
url = f"{self.api_base_url}/quote/"
|
|
83
129
|
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"amount1": amount1,
|
|
130
|
+
params: dict[str, Any] = {
|
|
131
|
+
"from_token": from_token,
|
|
132
|
+
"to_token": to_token,
|
|
133
|
+
"from_chain": from_chain,
|
|
134
|
+
"to_chain": to_chain,
|
|
135
|
+
"from_wallet": from_wallet,
|
|
136
|
+
"from_amount": from_amount,
|
|
92
137
|
}
|
|
93
138
|
|
|
94
|
-
# Only add optional parameters if they're provided
|
|
95
|
-
if slippage is not None:
|
|
96
|
-
payload["slippage"] = slippage
|
|
97
|
-
if wayfinder_fee is not None:
|
|
98
|
-
payload["wayfinder_fee"] = wayfinder_fee
|
|
99
|
-
|
|
100
139
|
try:
|
|
101
|
-
response = await self.
|
|
140
|
+
response = await self._authed_request("GET", url, params=params, headers={})
|
|
102
141
|
response.raise_for_status()
|
|
103
142
|
data = response.json()
|
|
143
|
+
result = data.get("data", data)
|
|
144
|
+
|
|
104
145
|
elapsed = time.time() - start_time
|
|
105
146
|
logger.info(f"BRAP quote request completed successfully in {elapsed:.2f}s")
|
|
106
|
-
return
|
|
147
|
+
return result
|
|
107
148
|
except Exception as e:
|
|
108
149
|
elapsed = time.time() - start_time
|
|
109
150
|
logger.error(f"BRAP quote request failed after {elapsed:.2f}s: {e}")
|
|
@@ -5,7 +5,6 @@ Consolidated client management for all API interactions
|
|
|
5
5
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
9
8
|
from wayfinder_paths.core.clients.BRAPClient import BRAPClient
|
|
10
9
|
from wayfinder_paths.core.clients.HyperlendClient import HyperlendClient
|
|
11
10
|
from wayfinder_paths.core.clients.LedgerClient import LedgerClient
|
|
@@ -37,7 +36,6 @@ class ClientManager:
|
|
|
37
36
|
self,
|
|
38
37
|
clients: dict[str, Any] | None = None,
|
|
39
38
|
skip_auth: bool = False,
|
|
40
|
-
api_key: str | None = None,
|
|
41
39
|
):
|
|
42
40
|
"""
|
|
43
41
|
Initialize ClientManager.
|
|
@@ -45,14 +43,10 @@ class ClientManager:
|
|
|
45
43
|
Args:
|
|
46
44
|
clients: Optional dict of pre-instantiated clients to inject directly.
|
|
47
45
|
skip_auth: If True, skips authentication (for SDK usage).
|
|
48
|
-
api_key: Optional API key for service account authentication.
|
|
49
46
|
"""
|
|
50
47
|
self._injected_clients = clients or {}
|
|
51
48
|
self._skip_auth = skip_auth
|
|
52
|
-
self._api_key = api_key
|
|
53
|
-
self._access_token: str | None = None
|
|
54
49
|
|
|
55
|
-
self._auth_client: AuthClient | None = None
|
|
56
50
|
self._token_client: TokenClientProtocol | None = None
|
|
57
51
|
self._wallet_client: WalletClientProtocol | None = None
|
|
58
52
|
self._ledger_client: LedgerClientProtocol | None = None
|
|
@@ -79,25 +73,10 @@ class ClientManager:
|
|
|
79
73
|
"""
|
|
80
74
|
client = getattr(self, client_attr)
|
|
81
75
|
if not client:
|
|
82
|
-
client = self._injected_clients.get(injected_key) or client_class(
|
|
83
|
-
api_key=self._api_key
|
|
84
|
-
)
|
|
76
|
+
client = self._injected_clients.get(injected_key) or client_class()
|
|
85
77
|
setattr(self, client_attr, client)
|
|
86
|
-
if self._access_token and hasattr(client, "set_bearer_token"):
|
|
87
|
-
client.set_bearer_token(self._access_token)
|
|
88
78
|
return client
|
|
89
79
|
|
|
90
|
-
@property
|
|
91
|
-
def auth(self) -> AuthClient | None:
|
|
92
|
-
"""Get or create auth client. Returns None if skip_auth=True."""
|
|
93
|
-
if self._skip_auth:
|
|
94
|
-
return None
|
|
95
|
-
if not self._auth_client:
|
|
96
|
-
self._auth_client = AuthClient(api_key=self._api_key)
|
|
97
|
-
if self._access_token:
|
|
98
|
-
self._auth_client.set_bearer_token(self._access_token)
|
|
99
|
-
return self._auth_client
|
|
100
|
-
|
|
101
80
|
@property
|
|
102
81
|
def token(self) -> TokenClientProtocol:
|
|
103
82
|
"""Get or create token client"""
|
|
@@ -130,55 +109,9 @@ class ClientManager:
|
|
|
130
109
|
"""Get or create BRAP client"""
|
|
131
110
|
return self._get_or_create_client("_brap_client", "brap", BRAPClient)
|
|
132
111
|
|
|
133
|
-
async def authenticate(
|
|
134
|
-
self,
|
|
135
|
-
username: str | None = None,
|
|
136
|
-
password: str | None = None,
|
|
137
|
-
*,
|
|
138
|
-
refresh_token: str | None = None,
|
|
139
|
-
) -> dict[str, Any]:
|
|
140
|
-
"""Authenticate with the API. Raises ValueError if skip_auth=True."""
|
|
141
|
-
if self._skip_auth:
|
|
142
|
-
raise ValueError(
|
|
143
|
-
"Authentication is disabled in SDK mode. SDK users handle their own authentication."
|
|
144
|
-
)
|
|
145
|
-
auth_client = self.auth
|
|
146
|
-
if auth_client is None:
|
|
147
|
-
raise ValueError("Auth client is not available")
|
|
148
|
-
data = await auth_client.authenticate(
|
|
149
|
-
username, password, refresh_token=refresh_token
|
|
150
|
-
)
|
|
151
|
-
access = data.get("access") or data.get("access_token")
|
|
152
|
-
if access:
|
|
153
|
-
self.set_access_token(access)
|
|
154
|
-
return data
|
|
155
|
-
|
|
156
|
-
def set_access_token(self, access_token: str) -> None:
|
|
157
|
-
"""Set and propagate access token to all initialized clients."""
|
|
158
|
-
self._access_token = access_token
|
|
159
|
-
if self._auth_client:
|
|
160
|
-
self._auth_client.set_bearer_token(access_token)
|
|
161
|
-
if self._token_client and hasattr(self._token_client, "set_bearer_token"):
|
|
162
|
-
self._token_client.set_bearer_token(access_token)
|
|
163
|
-
if self._transaction_client and hasattr(
|
|
164
|
-
self._transaction_client, "set_bearer_token"
|
|
165
|
-
):
|
|
166
|
-
self._transaction_client.set_bearer_token(access_token)
|
|
167
|
-
if self._ledger_client and hasattr(self._ledger_client, "set_bearer_token"):
|
|
168
|
-
self._ledger_client.set_bearer_token(access_token)
|
|
169
|
-
if self._pool_client and hasattr(self._pool_client, "set_bearer_token"):
|
|
170
|
-
self._pool_client.set_bearer_token(access_token)
|
|
171
|
-
if self._hyperlend_client and hasattr(
|
|
172
|
-
self._hyperlend_client, "set_bearer_token"
|
|
173
|
-
):
|
|
174
|
-
self._hyperlend_client.set_bearer_token(access_token)
|
|
175
|
-
if self._wallet_client and hasattr(self._wallet_client, "set_bearer_token"):
|
|
176
|
-
self._wallet_client.set_bearer_token(access_token)
|
|
177
|
-
|
|
178
112
|
def get_all_clients(self) -> dict[str, Any]:
|
|
179
113
|
"""Get all initialized clients for direct access"""
|
|
180
114
|
return {
|
|
181
|
-
"auth": self._auth_client,
|
|
182
115
|
"token": self._token_client,
|
|
183
116
|
"transaction": self._transaction_client,
|
|
184
117
|
"ledger": self._ledger_client,
|
|
@@ -1,97 +1,163 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Hyperlend Client
|
|
3
|
-
Provides access to Hyperlend stable markets data via
|
|
3
|
+
Provides access to Hyperlend stable markets data via blockchain endpoints.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from typing import Any,
|
|
8
|
+
from typing import Any, Required, TypedDict
|
|
9
9
|
|
|
10
10
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
11
11
|
from wayfinder_paths.core.config import get_api_base_url
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class StableMarket(TypedDict):
|
|
15
|
-
"""Stable market data structure"""
|
|
16
|
-
|
|
17
|
-
chain_id: Required[int]
|
|
18
|
-
token_address: Required[str]
|
|
19
|
-
symbol: Required[str]
|
|
20
|
-
name: Required[str]
|
|
21
|
-
underlying_tokens: Required[float]
|
|
22
|
-
buffer_bps: Required[int]
|
|
23
|
-
min_buffer_tokens: Required[float]
|
|
24
|
-
is_stable_symbol: Required[bool]
|
|
25
|
-
|
|
26
|
-
|
|
27
14
|
class AssetsView(TypedDict):
|
|
28
15
|
"""Assets view response structure"""
|
|
29
16
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
block_number: Required[int]
|
|
18
|
+
user: Required[str]
|
|
19
|
+
native_balance_wei: Required[int]
|
|
20
|
+
native_balance: Required[float]
|
|
21
|
+
assets: Required[list[AssetInfo]]
|
|
22
|
+
account_data: Required[AccountData]
|
|
23
|
+
base_currency_info: Required[BaseCurrencyInfo]
|
|
34
24
|
|
|
35
25
|
|
|
36
26
|
class MarketEntry(TypedDict):
|
|
37
27
|
"""Market entry data structure"""
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
symbol: Required[str]
|
|
30
|
+
symbol_canonical: Required[str]
|
|
31
|
+
display_symbol: Required[str]
|
|
32
|
+
reserve: Required[dict[str, Any]]
|
|
42
33
|
|
|
43
34
|
|
|
44
35
|
class LendRateHistory(TypedDict):
|
|
45
36
|
"""Lend rate history response structure"""
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
history: Required[list[RateHistoryEntry]]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MarketHeadroom(TypedDict):
|
|
42
|
+
"""Market headroom data structure"""
|
|
43
|
+
|
|
44
|
+
symbol: Required[str]
|
|
45
|
+
symbol_canonical: Required[str]
|
|
46
|
+
display_symbol: Required[str]
|
|
47
|
+
reserve: Required[dict[str, Any]]
|
|
48
|
+
decimals: Required[int]
|
|
49
|
+
headroom: Required[int]
|
|
50
|
+
supply_cap: Required[int]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class StableMarketsHeadroomResponse(TypedDict):
|
|
54
|
+
"""Stable markets headroom response structure"""
|
|
55
|
+
|
|
56
|
+
markets: Required[dict[str, MarketHeadroom]]
|
|
57
|
+
notes: Required[list[str]]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class RateHistoryEntry(TypedDict):
|
|
61
|
+
"""Rate history entry data structure"""
|
|
62
|
+
|
|
63
|
+
timestamp_ms: Required[int]
|
|
64
|
+
timestamp: Required[float]
|
|
65
|
+
supply_apr: Required[float]
|
|
66
|
+
supply_apy: Required[float]
|
|
67
|
+
borrow_apr: Required[float]
|
|
68
|
+
borrow_apy: Required[float]
|
|
69
|
+
token: Required[str]
|
|
70
|
+
symbol: Required[str]
|
|
71
|
+
display_symbol: Required[str]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AssetInfo(TypedDict):
|
|
75
|
+
"""Asset information structure"""
|
|
76
|
+
|
|
77
|
+
underlying: Required[str]
|
|
78
|
+
symbol: Required[str]
|
|
79
|
+
symbol_canonical: Required[str]
|
|
80
|
+
symbol_display: Required[str]
|
|
81
|
+
decimals: Required[int]
|
|
82
|
+
a_token: Required[str]
|
|
83
|
+
variable_debt_token: Required[str]
|
|
84
|
+
usage_as_collateral_enabled: Required[bool]
|
|
85
|
+
borrowing_enabled: Required[bool]
|
|
86
|
+
is_active: Required[bool]
|
|
87
|
+
is_frozen: Required[bool]
|
|
88
|
+
is_paused: Required[bool]
|
|
89
|
+
is_siloed_borrowing: Required[bool]
|
|
90
|
+
is_stablecoin: Required[bool]
|
|
91
|
+
underlying_wallet_balance: Required[float]
|
|
92
|
+
underlying_wallet_balance_wei: Required[int]
|
|
93
|
+
price_usd: Required[float]
|
|
94
|
+
supply: Required[float]
|
|
95
|
+
variable_borrow: Required[float]
|
|
96
|
+
supply_usd: Required[float]
|
|
97
|
+
variable_borrow_usd: Required[float]
|
|
98
|
+
supply_apr: Required[float]
|
|
99
|
+
supply_apy: Required[float]
|
|
100
|
+
variable_borrow_apr: Required[float]
|
|
101
|
+
variable_borrow_apy: Required[float]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class AccountData(TypedDict):
|
|
105
|
+
"""Account data structure"""
|
|
106
|
+
|
|
107
|
+
total_collateral_base: Required[int | float]
|
|
108
|
+
total_debt_base: Required[int | float]
|
|
109
|
+
available_borrows_base: Required[int | float]
|
|
110
|
+
current_liquidation_threshold: Required[int | float]
|
|
111
|
+
ltv: Required[int | float]
|
|
112
|
+
health_factor_wad: Required[int]
|
|
113
|
+
health_factor: Required[float]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class BaseCurrencyInfo(TypedDict):
|
|
117
|
+
"""Base currency information structure"""
|
|
118
|
+
|
|
119
|
+
marketReferenceCurrencyUnit: Required[int]
|
|
120
|
+
marketReferenceCurrencyPriceInUsd: Required[int]
|
|
121
|
+
networkBaseTokenPriceInUsd: Required[int]
|
|
122
|
+
networkBaseTokenPriceDecimals: Required[int]
|
|
51
123
|
|
|
52
124
|
|
|
53
125
|
class HyperlendClient(WayfinderClient):
|
|
54
126
|
"""Client for Hyperlend-related operations"""
|
|
55
127
|
|
|
56
|
-
def __init__(self
|
|
57
|
-
super().__init__(
|
|
128
|
+
def __init__(self):
|
|
129
|
+
super().__init__()
|
|
58
130
|
self.api_base_url = get_api_base_url()
|
|
59
131
|
|
|
60
132
|
async def get_stable_markets(
|
|
61
133
|
self,
|
|
62
134
|
*,
|
|
63
|
-
chain_id: int,
|
|
64
135
|
required_underlying_tokens: float | None = None,
|
|
65
136
|
buffer_bps: int | None = None,
|
|
66
137
|
min_buffer_tokens: float | None = None,
|
|
67
|
-
|
|
68
|
-
) -> list[StableMarket]:
|
|
138
|
+
) -> StableMarketsHeadroomResponse:
|
|
69
139
|
"""
|
|
70
|
-
Fetch stable markets from Hyperlend.
|
|
140
|
+
Fetch stable markets headroom from Hyperlend.
|
|
71
141
|
|
|
72
142
|
Args:
|
|
73
|
-
chain_id: Chain ID to query markets for
|
|
74
143
|
required_underlying_tokens: Required underlying tokens amount
|
|
75
144
|
buffer_bps: Buffer in basis points
|
|
76
145
|
min_buffer_tokens: Minimum buffer in tokens
|
|
77
|
-
is_stable_symbol: Filter by stable symbol (optional)
|
|
78
146
|
|
|
79
147
|
Example:
|
|
80
|
-
GET /api/v1/
|
|
148
|
+
GET /api/v1/blockchain/hyperlend/stable_markets_headroom/?required_underlying_tokens=1000&buffer_bps=50&min_buffer_tokens=100
|
|
81
149
|
|
|
82
150
|
Returns:
|
|
83
|
-
Dictionary containing stable markets data
|
|
151
|
+
Dictionary containing stable markets headroom data with markets and notes
|
|
84
152
|
"""
|
|
85
|
-
url = f"{self.api_base_url}/
|
|
86
|
-
params: dict[str, Any] = {
|
|
153
|
+
url = f"{self.api_base_url}/v1/blockchain/hyperlend/stable_markets_headroom/"
|
|
154
|
+
params: dict[str, Any] = {}
|
|
87
155
|
if required_underlying_tokens is not None:
|
|
88
156
|
params["required_underlying_tokens"] = required_underlying_tokens
|
|
89
157
|
if buffer_bps is not None:
|
|
90
158
|
params["buffer_bps"] = buffer_bps
|
|
91
159
|
if min_buffer_tokens is not None:
|
|
92
160
|
params["min_buffer_tokens"] = min_buffer_tokens
|
|
93
|
-
if is_stable_symbol is not None:
|
|
94
|
-
params["is_stable_symbol"] = "true" if is_stable_symbol else "false"
|
|
95
161
|
|
|
96
162
|
response = await self._authed_request("GET", url, params=params, headers={})
|
|
97
163
|
response.raise_for_status()
|
|
@@ -101,25 +167,22 @@ class HyperlendClient(WayfinderClient):
|
|
|
101
167
|
async def get_assets_view(
|
|
102
168
|
self,
|
|
103
169
|
*,
|
|
104
|
-
chain_id: int,
|
|
105
170
|
user_address: str,
|
|
106
171
|
) -> AssetsView:
|
|
107
172
|
"""
|
|
108
173
|
Fetch assets view for a user address from Hyperlend.
|
|
109
174
|
|
|
110
175
|
Args:
|
|
111
|
-
chain_id: Chain ID to query assets for
|
|
112
176
|
user_address: User wallet address to query assets for
|
|
113
177
|
|
|
114
178
|
Example:
|
|
115
|
-
GET /api/v1/
|
|
179
|
+
GET /api/v1/blockchain/hyperlend/assets/?user_address=0x0c737cB5934afCb5B01965141F865F795B324080
|
|
116
180
|
|
|
117
181
|
Returns:
|
|
118
|
-
Dictionary containing assets view data
|
|
182
|
+
Dictionary containing assets view data with account information and base currency info
|
|
119
183
|
"""
|
|
120
|
-
url = f"{self.api_base_url}/
|
|
184
|
+
url = f"{self.api_base_url}/v1/blockchain/hyperlend/assets/"
|
|
121
185
|
params: dict[str, Any] = {
|
|
122
|
-
"chain_id": chain_id,
|
|
123
186
|
"user_address": user_address,
|
|
124
187
|
}
|
|
125
188
|
|
|
@@ -131,26 +194,23 @@ class HyperlendClient(WayfinderClient):
|
|
|
131
194
|
async def get_market_entry(
|
|
132
195
|
self,
|
|
133
196
|
*,
|
|
134
|
-
|
|
135
|
-
token_address: str,
|
|
197
|
+
token: str,
|
|
136
198
|
) -> MarketEntry:
|
|
137
199
|
"""
|
|
138
200
|
Fetch market entry from Hyperlend.
|
|
139
201
|
|
|
140
202
|
Args:
|
|
141
|
-
|
|
142
|
-
token_address: Token address to query market for
|
|
203
|
+
token: Token address to query market for
|
|
143
204
|
|
|
144
205
|
Example:
|
|
145
|
-
GET /api/v1/
|
|
206
|
+
GET /api/v1/blockchain/hyperlend/market_entry/?token=0x5555555555555555555555555555555555555555
|
|
146
207
|
|
|
147
208
|
Returns:
|
|
148
|
-
Dictionary containing market entry data
|
|
209
|
+
Dictionary containing market entry data with symbol and reserve information
|
|
149
210
|
"""
|
|
150
|
-
url = f"{self.api_base_url}/
|
|
211
|
+
url = f"{self.api_base_url}/v1/blockchain/hyperlend/market_entry/"
|
|
151
212
|
params: dict[str, Any] = {
|
|
152
|
-
"
|
|
153
|
-
"token_address": token_address,
|
|
213
|
+
"token": token,
|
|
154
214
|
}
|
|
155
215
|
|
|
156
216
|
response = await self._authed_request("GET", url, params=params, headers={})
|
|
@@ -161,30 +221,31 @@ class HyperlendClient(WayfinderClient):
|
|
|
161
221
|
async def get_lend_rate_history(
|
|
162
222
|
self,
|
|
163
223
|
*,
|
|
164
|
-
|
|
165
|
-
token_address: str,
|
|
224
|
+
token: str,
|
|
166
225
|
lookback_hours: int,
|
|
226
|
+
force_refresh: bool | None = None,
|
|
167
227
|
) -> LendRateHistory:
|
|
168
228
|
"""
|
|
169
229
|
Fetch lend rate history from Hyperlend.
|
|
170
230
|
|
|
171
231
|
Args:
|
|
172
|
-
|
|
173
|
-
token_address: Token address to query rate history for
|
|
232
|
+
token: Token address to query rate history for
|
|
174
233
|
lookback_hours: Number of hours to look back for rate history
|
|
234
|
+
force_refresh: Whether to force refresh the data (optional)
|
|
175
235
|
|
|
176
236
|
Example:
|
|
177
|
-
GET /api/v1/
|
|
237
|
+
GET /api/v1/blockchain/hyperlend/lend_rate_history/?token=0x5555555555555555555555555555555555555555&lookback_hours=24&force_refresh=false
|
|
178
238
|
|
|
179
239
|
Returns:
|
|
180
|
-
Dictionary containing lend rate history data
|
|
240
|
+
Dictionary containing lend rate history data with history array
|
|
181
241
|
"""
|
|
182
|
-
url = f"{self.api_base_url}/
|
|
242
|
+
url = f"{self.api_base_url}/v1/blockchain/hyperlend/lend_rate_history/"
|
|
183
243
|
params: dict[str, Any] = {
|
|
184
|
-
"
|
|
185
|
-
"token_address": token_address,
|
|
244
|
+
"token": token,
|
|
186
245
|
"lookback_hours": lookback_hours,
|
|
187
246
|
}
|
|
247
|
+
if force_refresh is not None:
|
|
248
|
+
params["force_refresh"] = "true" if force_refresh else "false"
|
|
188
249
|
|
|
189
250
|
response = await self._authed_request("GET", url, params=params, headers={})
|
|
190
251
|
response.raise_for_status()
|
|
@@ -63,14 +63,11 @@ class LedgerClient:
|
|
|
63
63
|
- POST record snapshot
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
|
-
def __init__(
|
|
67
|
-
self, api_key: str | None = None, ledger_dir: Path | str | None = None
|
|
68
|
-
) -> None:
|
|
66
|
+
def __init__(self, ledger_dir: Path | str | None = None) -> None:
|
|
69
67
|
"""
|
|
70
68
|
Initialize the ledger client.
|
|
71
69
|
|
|
72
70
|
Args:
|
|
73
|
-
api_key: Unused, kept for backward compatibility
|
|
74
71
|
ledger_dir: Directory to store ledger JSON files. Defaults to .ledger in project root
|
|
75
72
|
"""
|
|
76
73
|
if ledger_dir is None:
|