wayfinder-paths 0.1.1__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/CONFIG_GUIDE.md +394 -0
- wayfinder_paths/__init__.py +21 -0
- wayfinder_paths/config.example.json +20 -0
- wayfinder_paths/conftest.py +31 -0
- wayfinder_paths/core/__init__.py +13 -0
- wayfinder_paths/core/adapters/BaseAdapter.py +48 -0
- wayfinder_paths/core/adapters/__init__.py +5 -0
- wayfinder_paths/core/adapters/base.py +5 -0
- wayfinder_paths/core/clients/AuthClient.py +83 -0
- wayfinder_paths/core/clients/BRAPClient.py +90 -0
- wayfinder_paths/core/clients/ClientManager.py +231 -0
- wayfinder_paths/core/clients/HyperlendClient.py +151 -0
- wayfinder_paths/core/clients/LedgerClient.py +222 -0
- wayfinder_paths/core/clients/PoolClient.py +96 -0
- wayfinder_paths/core/clients/SimulationClient.py +180 -0
- wayfinder_paths/core/clients/TokenClient.py +73 -0
- wayfinder_paths/core/clients/TransactionClient.py +47 -0
- wayfinder_paths/core/clients/WalletClient.py +90 -0
- wayfinder_paths/core/clients/WayfinderClient.py +258 -0
- wayfinder_paths/core/clients/__init__.py +48 -0
- wayfinder_paths/core/clients/protocols.py +295 -0
- wayfinder_paths/core/clients/sdk_example.py +115 -0
- wayfinder_paths/core/config.py +369 -0
- wayfinder_paths/core/constants/__init__.py +26 -0
- wayfinder_paths/core/constants/base.py +25 -0
- wayfinder_paths/core/constants/erc20_abi.py +118 -0
- wayfinder_paths/core/constants/hyperlend_abi.py +152 -0
- wayfinder_paths/core/engine/VaultJob.py +182 -0
- wayfinder_paths/core/engine/__init__.py +5 -0
- wayfinder_paths/core/engine/manifest.py +97 -0
- wayfinder_paths/core/services/__init__.py +0 -0
- wayfinder_paths/core/services/base.py +177 -0
- wayfinder_paths/core/services/local_evm_txn.py +429 -0
- wayfinder_paths/core/services/local_token_txn.py +231 -0
- wayfinder_paths/core/services/web3_service.py +45 -0
- wayfinder_paths/core/settings.py +61 -0
- wayfinder_paths/core/strategies/Strategy.py +183 -0
- wayfinder_paths/core/strategies/__init__.py +5 -0
- wayfinder_paths/core/strategies/base.py +7 -0
- wayfinder_paths/core/utils/__init__.py +1 -0
- wayfinder_paths/core/utils/evm_helpers.py +165 -0
- wayfinder_paths/core/utils/wallets.py +77 -0
- wayfinder_paths/core/wallets/README.md +91 -0
- wayfinder_paths/core/wallets/WalletManager.py +56 -0
- wayfinder_paths/core/wallets/__init__.py +7 -0
- wayfinder_paths/run_strategy.py +409 -0
- wayfinder_paths/scripts/__init__.py +0 -0
- wayfinder_paths/scripts/create_strategy.py +181 -0
- wayfinder_paths/scripts/make_wallets.py +160 -0
- wayfinder_paths/scripts/validate_manifests.py +213 -0
- wayfinder_paths/tests/__init__.py +0 -0
- wayfinder_paths/tests/test_smoke_manifest.py +48 -0
- wayfinder_paths/tests/test_test_coverage.py +212 -0
- wayfinder_paths/tests/test_utils.py +64 -0
- wayfinder_paths/vaults/__init__.py +0 -0
- wayfinder_paths/vaults/adapters/__init__.py +0 -0
- wayfinder_paths/vaults/adapters/balance_adapter/README.md +104 -0
- wayfinder_paths/vaults/adapters/balance_adapter/adapter.py +257 -0
- wayfinder_paths/vaults/adapters/balance_adapter/examples.json +6 -0
- wayfinder_paths/vaults/adapters/balance_adapter/manifest.yaml +8 -0
- wayfinder_paths/vaults/adapters/balance_adapter/test_adapter.py +83 -0
- wayfinder_paths/vaults/adapters/brap_adapter/README.md +249 -0
- wayfinder_paths/vaults/adapters/brap_adapter/__init__.py +7 -0
- wayfinder_paths/vaults/adapters/brap_adapter/adapter.py +717 -0
- wayfinder_paths/vaults/adapters/brap_adapter/examples.json +175 -0
- wayfinder_paths/vaults/adapters/brap_adapter/manifest.yaml +11 -0
- wayfinder_paths/vaults/adapters/brap_adapter/test_adapter.py +288 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +7 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/adapter.py +298 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/manifest.yaml +10 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/test_adapter.py +267 -0
- wayfinder_paths/vaults/adapters/ledger_adapter/README.md +158 -0
- wayfinder_paths/vaults/adapters/ledger_adapter/__init__.py +7 -0
- wayfinder_paths/vaults/adapters/ledger_adapter/adapter.py +286 -0
- wayfinder_paths/vaults/adapters/ledger_adapter/examples.json +131 -0
- wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +11 -0
- wayfinder_paths/vaults/adapters/ledger_adapter/test_adapter.py +202 -0
- wayfinder_paths/vaults/adapters/pool_adapter/README.md +218 -0
- wayfinder_paths/vaults/adapters/pool_adapter/__init__.py +7 -0
- wayfinder_paths/vaults/adapters/pool_adapter/adapter.py +289 -0
- wayfinder_paths/vaults/adapters/pool_adapter/examples.json +143 -0
- wayfinder_paths/vaults/adapters/pool_adapter/manifest.yaml +10 -0
- wayfinder_paths/vaults/adapters/pool_adapter/test_adapter.py +222 -0
- wayfinder_paths/vaults/adapters/token_adapter/README.md +101 -0
- wayfinder_paths/vaults/adapters/token_adapter/__init__.py +3 -0
- wayfinder_paths/vaults/adapters/token_adapter/adapter.py +92 -0
- wayfinder_paths/vaults/adapters/token_adapter/examples.json +26 -0
- wayfinder_paths/vaults/adapters/token_adapter/manifest.yaml +6 -0
- wayfinder_paths/vaults/adapters/token_adapter/test_adapter.py +135 -0
- wayfinder_paths/vaults/strategies/__init__.py +0 -0
- wayfinder_paths/vaults/strategies/config.py +85 -0
- wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/README.md +99 -0
- wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/examples.json +16 -0
- wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/manifest.yaml +7 -0
- wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/strategy.py +2328 -0
- wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/test_strategy.py +319 -0
- wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/README.md +95 -0
- wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/examples.json +17 -0
- wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/manifest.yaml +17 -0
- wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/strategy.py +1684 -0
- wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/test_strategy.py +350 -0
- wayfinder_paths/vaults/templates/adapter/README.md +105 -0
- wayfinder_paths/vaults/templates/adapter/adapter.py +26 -0
- wayfinder_paths/vaults/templates/adapter/examples.json +8 -0
- wayfinder_paths/vaults/templates/adapter/manifest.yaml +6 -0
- wayfinder_paths/vaults/templates/adapter/test_adapter.py +49 -0
- wayfinder_paths/vaults/templates/strategy/README.md +152 -0
- wayfinder_paths/vaults/templates/strategy/examples.json +11 -0
- wayfinder_paths/vaults/templates/strategy/manifest.yaml +8 -0
- wayfinder_paths/vaults/templates/strategy/strategy.py +57 -0
- wayfinder_paths/vaults/templates/strategy/test_strategy.py +197 -0
- wayfinder_paths-0.1.1.dist-info/LICENSE +21 -0
- wayfinder_paths-0.1.1.dist-info/METADATA +727 -0
- wayfinder_paths-0.1.1.dist-info/RECORD +115 -0
- wayfinder_paths-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BRAP (Bridge/Router/Adapter Protocol) Client
|
|
3
|
+
Provides access to quote operations via the public quote endpoint.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from loguru import logger
|
|
10
|
+
|
|
11
|
+
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
12
|
+
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
13
|
+
from wayfinder_paths.core.settings import settings
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BRAPClient(WayfinderClient):
|
|
17
|
+
"""Client for BRAP quote operations"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, api_key: str | None = None):
|
|
20
|
+
super().__init__(api_key=api_key)
|
|
21
|
+
self.api_base_url = f"{settings.WAYFINDER_API_URL}"
|
|
22
|
+
self._auth_client: AuthClient | None = AuthClient(api_key=api_key)
|
|
23
|
+
|
|
24
|
+
async def get_quote(
|
|
25
|
+
self,
|
|
26
|
+
*,
|
|
27
|
+
from_token_address: str,
|
|
28
|
+
to_token_address: str,
|
|
29
|
+
from_chain_id: int,
|
|
30
|
+
to_chain_id: int,
|
|
31
|
+
from_address: str,
|
|
32
|
+
to_address: str,
|
|
33
|
+
amount1: str,
|
|
34
|
+
slippage: float | None = None,
|
|
35
|
+
wayfinder_fee: float | None = None,
|
|
36
|
+
) -> dict[str, Any]:
|
|
37
|
+
"""
|
|
38
|
+
Get a quote for a bridge/swap operation.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
from_token_address: Source token contract address
|
|
42
|
+
to_token_address: Destination token contract address
|
|
43
|
+
from_chain_id: Source chain ID
|
|
44
|
+
to_chain_id: Destination chain ID
|
|
45
|
+
from_address: Source wallet address
|
|
46
|
+
to_address: Destination wallet address
|
|
47
|
+
amount1: Amount to swap (in smallest units)
|
|
48
|
+
slippage: Maximum slippage tolerance (optional)
|
|
49
|
+
wayfinder_fee: Wayfinder fee (optional)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Quote data including routes, amounts, fees, etc.
|
|
53
|
+
"""
|
|
54
|
+
logger.info(
|
|
55
|
+
f"Getting BRAP quote: {from_token_address} -> {to_token_address} (chain {from_chain_id} -> {to_chain_id})"
|
|
56
|
+
)
|
|
57
|
+
logger.debug(
|
|
58
|
+
f"Quote params: amount={amount1}, slippage={slippage}, wayfinder_fee={wayfinder_fee}"
|
|
59
|
+
)
|
|
60
|
+
start_time = time.time()
|
|
61
|
+
|
|
62
|
+
url = f"{self.api_base_url}/public/quotes/"
|
|
63
|
+
|
|
64
|
+
payload = {
|
|
65
|
+
"from_token_address": from_token_address,
|
|
66
|
+
"to_token_address": to_token_address,
|
|
67
|
+
"from_chain_id": from_chain_id,
|
|
68
|
+
"to_chain_id": to_chain_id,
|
|
69
|
+
"from_address": from_address,
|
|
70
|
+
"to_address": to_address,
|
|
71
|
+
"amount1": amount1,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Only add optional parameters if they're provided
|
|
75
|
+
if slippage is not None:
|
|
76
|
+
payload["slippage"] = slippage
|
|
77
|
+
if wayfinder_fee is not None:
|
|
78
|
+
payload["wayfinder_fee"] = wayfinder_fee
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
response = await self._request("POST", url, json=payload, headers={})
|
|
82
|
+
response.raise_for_status()
|
|
83
|
+
data = response.json()
|
|
84
|
+
elapsed = time.time() - start_time
|
|
85
|
+
logger.info(f"BRAP quote request completed successfully in {elapsed:.2f}s")
|
|
86
|
+
return data.get("data", data)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
elapsed = time.time() - start_time
|
|
89
|
+
logger.error(f"BRAP quote request failed after {elapsed:.2f}s: {e}")
|
|
90
|
+
raise
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Client Manager
|
|
3
|
+
Consolidated client management for all API interactions
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
9
|
+
from wayfinder_paths.core.clients.BRAPClient import BRAPClient
|
|
10
|
+
from wayfinder_paths.core.clients.HyperlendClient import HyperlendClient
|
|
11
|
+
from wayfinder_paths.core.clients.LedgerClient import LedgerClient
|
|
12
|
+
from wayfinder_paths.core.clients.PoolClient import PoolClient
|
|
13
|
+
from wayfinder_paths.core.clients.protocols import (
|
|
14
|
+
BRAPClientProtocol,
|
|
15
|
+
HyperlendClientProtocol,
|
|
16
|
+
LedgerClientProtocol,
|
|
17
|
+
PoolClientProtocol,
|
|
18
|
+
SimulationClientProtocol,
|
|
19
|
+
TokenClientProtocol,
|
|
20
|
+
TransactionClientProtocol,
|
|
21
|
+
WalletClientProtocol,
|
|
22
|
+
)
|
|
23
|
+
from wayfinder_paths.core.clients.SimulationClient import SimulationClient
|
|
24
|
+
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
25
|
+
from wayfinder_paths.core.clients.TransactionClient import TransactionClient
|
|
26
|
+
from wayfinder_paths.core.clients.WalletClient import WalletClient
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ClientManager:
|
|
30
|
+
"""
|
|
31
|
+
Manages all API client instances for a vault job.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
clients: Optional dict of pre-instantiated clients to inject directly.
|
|
35
|
+
Keys: 'token', 'hyperlend', 'ledger', 'wallet', 'transaction', 'pool', 'brap', 'simulation'.
|
|
36
|
+
If not provided, defaults to HTTP-based clients.
|
|
37
|
+
skip_auth: If True, skips authentication (for SDK usage).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
clients: dict[str, Any] | None = None,
|
|
43
|
+
skip_auth: bool = False,
|
|
44
|
+
api_key: str | None = None,
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Initialize ClientManager.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
clients: Optional dict of pre-instantiated clients to inject directly.
|
|
51
|
+
skip_auth: If True, skips authentication (for SDK usage).
|
|
52
|
+
api_key: Optional API key for service account authentication.
|
|
53
|
+
"""
|
|
54
|
+
self._injected_clients = clients or {}
|
|
55
|
+
self._skip_auth = skip_auth
|
|
56
|
+
self._api_key = api_key
|
|
57
|
+
self._access_token: str | None = None
|
|
58
|
+
|
|
59
|
+
self._auth_client: AuthClient | None = None
|
|
60
|
+
self._token_client: TokenClientProtocol | None = None
|
|
61
|
+
self._wallet_client: WalletClientProtocol | None = None
|
|
62
|
+
self._transaction_client: TransactionClientProtocol | None = None
|
|
63
|
+
self._ledger_client: LedgerClientProtocol | None = None
|
|
64
|
+
self._pool_client: PoolClientProtocol | None = None
|
|
65
|
+
self._hyperlend_client: HyperlendClientProtocol | None = None
|
|
66
|
+
self._brap_client: BRAPClientProtocol | None = None
|
|
67
|
+
self._simulation_client: SimulationClientProtocol | None = None
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def auth(self) -> AuthClient | None:
|
|
71
|
+
"""Get or create auth client. Returns None if skip_auth=True."""
|
|
72
|
+
if self._skip_auth:
|
|
73
|
+
return None
|
|
74
|
+
if not self._auth_client:
|
|
75
|
+
self._auth_client = AuthClient(api_key=self._api_key)
|
|
76
|
+
if self._access_token:
|
|
77
|
+
self._auth_client.set_bearer_token(self._access_token)
|
|
78
|
+
return self._auth_client
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def token(self) -> TokenClientProtocol:
|
|
82
|
+
"""Get or create token client"""
|
|
83
|
+
if not self._token_client:
|
|
84
|
+
self._token_client = self._injected_clients.get("token") or TokenClient(
|
|
85
|
+
api_key=self._api_key
|
|
86
|
+
)
|
|
87
|
+
if self._access_token and hasattr(self._token_client, "set_bearer_token"):
|
|
88
|
+
self._token_client.set_bearer_token(self._access_token)
|
|
89
|
+
return self._token_client
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def transaction(self) -> TransactionClientProtocol:
|
|
93
|
+
"""Get or create transaction client"""
|
|
94
|
+
if not self._transaction_client:
|
|
95
|
+
self._transaction_client = self._injected_clients.get(
|
|
96
|
+
"transaction"
|
|
97
|
+
) or TransactionClient(api_key=self._api_key)
|
|
98
|
+
if self._access_token and hasattr(
|
|
99
|
+
self._transaction_client, "set_bearer_token"
|
|
100
|
+
):
|
|
101
|
+
self._transaction_client.set_bearer_token(self._access_token)
|
|
102
|
+
return self._transaction_client
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def ledger(self) -> LedgerClientProtocol:
|
|
106
|
+
"""Get or create ledger client"""
|
|
107
|
+
if not self._ledger_client:
|
|
108
|
+
self._ledger_client = self._injected_clients.get("ledger") or LedgerClient(
|
|
109
|
+
api_key=self._api_key
|
|
110
|
+
)
|
|
111
|
+
if self._access_token and hasattr(self._ledger_client, "set_bearer_token"):
|
|
112
|
+
self._ledger_client.set_bearer_token(self._access_token)
|
|
113
|
+
return self._ledger_client
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def pool(self) -> PoolClientProtocol:
|
|
117
|
+
"""Get or create pool client"""
|
|
118
|
+
if not self._pool_client:
|
|
119
|
+
self._pool_client = self._injected_clients.get("pool") or PoolClient(
|
|
120
|
+
api_key=self._api_key
|
|
121
|
+
)
|
|
122
|
+
if self._access_token and hasattr(self._pool_client, "set_bearer_token"):
|
|
123
|
+
self._pool_client.set_bearer_token(self._access_token)
|
|
124
|
+
return self._pool_client
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def hyperlend(self) -> HyperlendClientProtocol:
|
|
128
|
+
"""Get or create hyperlend client"""
|
|
129
|
+
if not self._hyperlend_client:
|
|
130
|
+
self._hyperlend_client = self._injected_clients.get(
|
|
131
|
+
"hyperlend"
|
|
132
|
+
) or HyperlendClient(api_key=self._api_key)
|
|
133
|
+
if self._access_token and hasattr(
|
|
134
|
+
self._hyperlend_client, "set_bearer_token"
|
|
135
|
+
):
|
|
136
|
+
self._hyperlend_client.set_bearer_token(self._access_token)
|
|
137
|
+
return self._hyperlend_client
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def wallet(self) -> WalletClientProtocol:
|
|
141
|
+
"""Get or create wallet client"""
|
|
142
|
+
if not self._wallet_client:
|
|
143
|
+
self._wallet_client = self._injected_clients.get("wallet") or WalletClient(
|
|
144
|
+
api_key=self._api_key
|
|
145
|
+
)
|
|
146
|
+
if self._access_token and hasattr(self._wallet_client, "set_bearer_token"):
|
|
147
|
+
self._wallet_client.set_bearer_token(self._access_token)
|
|
148
|
+
return self._wallet_client
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def brap(self) -> BRAPClientProtocol:
|
|
152
|
+
"""Get or create BRAP client"""
|
|
153
|
+
if not self._brap_client:
|
|
154
|
+
self._brap_client = self._injected_clients.get("brap") or BRAPClient(
|
|
155
|
+
api_key=self._api_key
|
|
156
|
+
)
|
|
157
|
+
if self._access_token and hasattr(self._brap_client, "set_bearer_token"):
|
|
158
|
+
self._brap_client.set_bearer_token(self._access_token)
|
|
159
|
+
return self._brap_client
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def simulation(self) -> SimulationClientProtocol:
|
|
163
|
+
"""Get or create simulation client"""
|
|
164
|
+
if not self._simulation_client:
|
|
165
|
+
self._simulation_client = self._injected_clients.get(
|
|
166
|
+
"simulation"
|
|
167
|
+
) or SimulationClient(api_key=self._api_key)
|
|
168
|
+
if self._access_token and hasattr(
|
|
169
|
+
self._simulation_client, "set_bearer_token"
|
|
170
|
+
):
|
|
171
|
+
self._simulation_client.set_bearer_token(self._access_token)
|
|
172
|
+
return self._simulation_client
|
|
173
|
+
|
|
174
|
+
async def authenticate(
|
|
175
|
+
self,
|
|
176
|
+
username: str | None = None,
|
|
177
|
+
password: str | None = None,
|
|
178
|
+
*,
|
|
179
|
+
refresh_token: str | None = None,
|
|
180
|
+
) -> dict[str, Any]:
|
|
181
|
+
"""Authenticate with the API. Raises ValueError if skip_auth=True."""
|
|
182
|
+
if self._skip_auth:
|
|
183
|
+
raise ValueError(
|
|
184
|
+
"Authentication is disabled in SDK mode. SDK users handle their own authentication."
|
|
185
|
+
)
|
|
186
|
+
auth_client = self.auth
|
|
187
|
+
if auth_client is None:
|
|
188
|
+
raise ValueError("Auth client is not available")
|
|
189
|
+
data = await auth_client.authenticate(
|
|
190
|
+
username, password, refresh_token=refresh_token
|
|
191
|
+
)
|
|
192
|
+
access = data.get("access") or data.get("access_token")
|
|
193
|
+
if access:
|
|
194
|
+
self.set_access_token(access)
|
|
195
|
+
return data
|
|
196
|
+
|
|
197
|
+
def set_access_token(self, access_token: str) -> None:
|
|
198
|
+
"""Set and propagate access token to all initialized clients."""
|
|
199
|
+
self._access_token = access_token
|
|
200
|
+
if self._auth_client:
|
|
201
|
+
self._auth_client.set_bearer_token(access_token)
|
|
202
|
+
if self._token_client and hasattr(self._token_client, "set_bearer_token"):
|
|
203
|
+
self._token_client.set_bearer_token(access_token)
|
|
204
|
+
if self._transaction_client and hasattr(
|
|
205
|
+
self._transaction_client, "set_bearer_token"
|
|
206
|
+
):
|
|
207
|
+
self._transaction_client.set_bearer_token(access_token)
|
|
208
|
+
if self._ledger_client and hasattr(self._ledger_client, "set_bearer_token"):
|
|
209
|
+
self._ledger_client.set_bearer_token(access_token)
|
|
210
|
+
if self._pool_client and hasattr(self._pool_client, "set_bearer_token"):
|
|
211
|
+
self._pool_client.set_bearer_token(access_token)
|
|
212
|
+
if self._hyperlend_client and hasattr(
|
|
213
|
+
self._hyperlend_client, "set_bearer_token"
|
|
214
|
+
):
|
|
215
|
+
self._hyperlend_client.set_bearer_token(access_token)
|
|
216
|
+
if self._wallet_client and hasattr(self._wallet_client, "set_bearer_token"):
|
|
217
|
+
self._wallet_client.set_bearer_token(access_token)
|
|
218
|
+
|
|
219
|
+
def get_all_clients(self) -> dict[str, Any]:
|
|
220
|
+
"""Get all initialized clients for direct access"""
|
|
221
|
+
return {
|
|
222
|
+
"auth": self._auth_client,
|
|
223
|
+
"token": self._token_client,
|
|
224
|
+
"transaction": self._transaction_client,
|
|
225
|
+
"ledger": self._ledger_client,
|
|
226
|
+
"pool": self._pool_client,
|
|
227
|
+
"wallet": self._wallet_client,
|
|
228
|
+
"hyperlend": self._hyperlend_client,
|
|
229
|
+
"brap": self._brap_client,
|
|
230
|
+
"simulation": self._simulation_client,
|
|
231
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hyperlend Client
|
|
3
|
+
Provides access to Hyperlend stable markets data via public endpoints.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
9
|
+
from wayfinder_paths.core.settings import settings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HyperlendClient(WayfinderClient):
|
|
13
|
+
"""Client for Hyperlend-related operations"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, api_key: str | None = None):
|
|
16
|
+
super().__init__(api_key=api_key)
|
|
17
|
+
self.api_base_url = f"{settings.WAYFINDER_API_URL}"
|
|
18
|
+
|
|
19
|
+
async def get_stable_markets(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
chain_id: int,
|
|
23
|
+
required_underlying_tokens: float | None = None,
|
|
24
|
+
buffer_bps: int | None = None,
|
|
25
|
+
min_buffer_tokens: float | None = None,
|
|
26
|
+
is_stable_symbol: bool | None = None,
|
|
27
|
+
) -> dict[str, Any]:
|
|
28
|
+
"""
|
|
29
|
+
Fetch stable markets from Hyperlend.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
chain_id: Chain ID to query markets for
|
|
33
|
+
required_underlying_tokens: Required underlying tokens amount
|
|
34
|
+
buffer_bps: Buffer in basis points
|
|
35
|
+
min_buffer_tokens: Minimum buffer in tokens
|
|
36
|
+
is_stable_symbol: Filter by stable symbol (optional)
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
GET /api/v1/public/hyperlend/stable-markets/?chain_id=999&required_underlying_tokens=1000.0&buffer_bps=100&min_buffer_tokens=100.0&is_stable_symbol=true
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Dictionary containing stable markets data
|
|
43
|
+
"""
|
|
44
|
+
url = f"{self.api_base_url}/public/hyperlend/stable-markets/"
|
|
45
|
+
params: dict[str, Any] = {"chain_id": chain_id}
|
|
46
|
+
if required_underlying_tokens is not None:
|
|
47
|
+
params["required_underlying_tokens"] = required_underlying_tokens
|
|
48
|
+
if buffer_bps is not None:
|
|
49
|
+
params["buffer_bps"] = buffer_bps
|
|
50
|
+
if min_buffer_tokens is not None:
|
|
51
|
+
params["min_buffer_tokens"] = min_buffer_tokens
|
|
52
|
+
if is_stable_symbol is not None:
|
|
53
|
+
params["is_stable_symbol"] = "true" if is_stable_symbol else "false"
|
|
54
|
+
|
|
55
|
+
response = await self._authed_request("GET", url, params=params, headers={})
|
|
56
|
+
response.raise_for_status()
|
|
57
|
+
data = response.json()
|
|
58
|
+
return data.get("data", data)
|
|
59
|
+
|
|
60
|
+
async def get_assets_view(
|
|
61
|
+
self,
|
|
62
|
+
*,
|
|
63
|
+
chain_id: int,
|
|
64
|
+
user_address: str,
|
|
65
|
+
) -> dict[str, Any]:
|
|
66
|
+
"""
|
|
67
|
+
Fetch assets view for a user address from Hyperlend.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
chain_id: Chain ID to query assets for
|
|
71
|
+
user_address: User wallet address to query assets for
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
GET /api/v1/public/hyperlend/assets-view/?chain_id=999&user_address=0x0c737cB5934afCb5B01965141F865F795B324080
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dictionary containing assets view data
|
|
78
|
+
"""
|
|
79
|
+
url = f"{self.api_base_url}/public/hyperlend/assets-view/"
|
|
80
|
+
params: dict[str, Any] = {
|
|
81
|
+
"chain_id": chain_id,
|
|
82
|
+
"user_address": user_address,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
response = await self._authed_request("GET", url, params=params, headers={})
|
|
86
|
+
response.raise_for_status()
|
|
87
|
+
data = response.json()
|
|
88
|
+
return data.get("data", data)
|
|
89
|
+
|
|
90
|
+
async def get_market_entry(
|
|
91
|
+
self,
|
|
92
|
+
*,
|
|
93
|
+
chain_id: int,
|
|
94
|
+
token_address: str,
|
|
95
|
+
) -> dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Fetch market entry from Hyperlend.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
chain_id: Chain ID to query market for
|
|
101
|
+
token_address: Token address to query market for
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
GET /api/v1/public/hyperlend/market-entry/?chain_id=999&token_address=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Dictionary containing market entry data
|
|
108
|
+
"""
|
|
109
|
+
url = f"{self.api_base_url}/public/hyperlend/market-entry/"
|
|
110
|
+
params: dict[str, Any] = {
|
|
111
|
+
"chain_id": chain_id,
|
|
112
|
+
"token_address": token_address,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
response = await self._authed_request("GET", url, params=params, headers={})
|
|
116
|
+
response.raise_for_status()
|
|
117
|
+
data = response.json()
|
|
118
|
+
return data.get("data", data)
|
|
119
|
+
|
|
120
|
+
async def get_lend_rate_history(
|
|
121
|
+
self,
|
|
122
|
+
*,
|
|
123
|
+
chain_id: int,
|
|
124
|
+
token_address: str,
|
|
125
|
+
lookback_hours: int,
|
|
126
|
+
) -> dict[str, Any]:
|
|
127
|
+
"""
|
|
128
|
+
Fetch lend rate history from Hyperlend.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
chain_id: Chain ID to query rate history for
|
|
132
|
+
token_address: Token address to query rate history for
|
|
133
|
+
lookback_hours: Number of hours to look back for rate history
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
GET /api/v1/public/hyperlend/lend-rate-history/?chain_id=999&token_address=0x5555555555555555555555555555555555555555&lookback_hours=24
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Dictionary containing lend rate history data
|
|
140
|
+
"""
|
|
141
|
+
url = f"{self.api_base_url}/public/hyperlend/lend-rate-history/"
|
|
142
|
+
params: dict[str, Any] = {
|
|
143
|
+
"chain_id": chain_id,
|
|
144
|
+
"token_address": token_address,
|
|
145
|
+
"lookback_hours": lookback_hours,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
response = await self._authed_request("GET", url, params=params, headers={})
|
|
149
|
+
response.raise_for_status()
|
|
150
|
+
data = response.json()
|
|
151
|
+
return data.get("data", data)
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
4
|
+
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
5
|
+
from wayfinder_paths.core.settings import settings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LedgerClient(WayfinderClient):
|
|
9
|
+
"""
|
|
10
|
+
Client for vault transaction history and bookkeeping operations.
|
|
11
|
+
|
|
12
|
+
Supports:
|
|
13
|
+
- GET vault transactions
|
|
14
|
+
- GET vault net deposit
|
|
15
|
+
- GET vault last rotation time
|
|
16
|
+
- POST add deposit
|
|
17
|
+
- POST add withdraw
|
|
18
|
+
- POST add operation
|
|
19
|
+
- POST add cashflow
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, api_key: str | None = None) -> None:
|
|
23
|
+
super().__init__(api_key=api_key)
|
|
24
|
+
self.api_base_url = f"{settings.WAYFINDER_API_URL}"
|
|
25
|
+
self._auth_client: AuthClient | None = AuthClient(api_key=api_key)
|
|
26
|
+
|
|
27
|
+
# ===================== Read Endpoints =====================
|
|
28
|
+
|
|
29
|
+
async def get_vault_transactions(
|
|
30
|
+
self,
|
|
31
|
+
*,
|
|
32
|
+
wallet_address: str,
|
|
33
|
+
limit: int = 50,
|
|
34
|
+
offset: int = 0,
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
Fetch a paginated list of transactions for a given vault wallet and address.
|
|
38
|
+
|
|
39
|
+
GET /api/v1/public/vaults/transactions/?wallet_address=...&limit=...&offset=...
|
|
40
|
+
"""
|
|
41
|
+
url = f"{self.api_base_url}/public/vaults/transactions/"
|
|
42
|
+
params = {
|
|
43
|
+
"wallet_address": wallet_address,
|
|
44
|
+
"limit": str(limit),
|
|
45
|
+
"offset": str(offset),
|
|
46
|
+
}
|
|
47
|
+
response = await self._authed_request("GET", url, params=params)
|
|
48
|
+
data = response.json()
|
|
49
|
+
return data.get("data", data)
|
|
50
|
+
|
|
51
|
+
async def get_vault_net_deposit(self, *, wallet_address: str) -> dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Fetch the net deposit (deposits - withdrawals) for a vault and address.
|
|
54
|
+
|
|
55
|
+
GET /api/v1/public/vaults/net-deposit/?wallet_address=...
|
|
56
|
+
"""
|
|
57
|
+
url = f"{self.api_base_url}/public/vaults/net-deposit/"
|
|
58
|
+
params = {
|
|
59
|
+
"wallet_address": wallet_address,
|
|
60
|
+
}
|
|
61
|
+
response = await self._authed_request("GET", url, params=params)
|
|
62
|
+
data = response.json()
|
|
63
|
+
return data.get("data", data)
|
|
64
|
+
|
|
65
|
+
async def get_vault_latest_transactions(
|
|
66
|
+
self, *, wallet_address: str
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Fetch the last rotation time for a vault and address.
|
|
70
|
+
|
|
71
|
+
GET /api/v1/public/vaults/last-rotation-time/?wallet_address=...
|
|
72
|
+
"""
|
|
73
|
+
url = f"{self.api_base_url}/public/vaults/latest-transactions/"
|
|
74
|
+
params = {
|
|
75
|
+
"wallet_address": wallet_address,
|
|
76
|
+
}
|
|
77
|
+
response = await self._authed_request("GET", url, params=params)
|
|
78
|
+
data = response.json()
|
|
79
|
+
return data.get("data", data)
|
|
80
|
+
|
|
81
|
+
# ===================== Write Endpoints =====================
|
|
82
|
+
|
|
83
|
+
async def add_vault_deposit(
|
|
84
|
+
self,
|
|
85
|
+
*,
|
|
86
|
+
wallet_address: str,
|
|
87
|
+
chain_id: int,
|
|
88
|
+
token_address: str,
|
|
89
|
+
token_amount: str | float,
|
|
90
|
+
usd_value: str | float,
|
|
91
|
+
data: dict[str, Any] | None = None,
|
|
92
|
+
strategy_name: str | None = None,
|
|
93
|
+
) -> dict[str, Any]:
|
|
94
|
+
"""
|
|
95
|
+
Record a deposit for a vault.
|
|
96
|
+
|
|
97
|
+
POST /api/v1/public/vaults/deposits/
|
|
98
|
+
"""
|
|
99
|
+
url = f"{self.api_base_url}/public/vaults/deposits/"
|
|
100
|
+
payload: dict[str, Any] = {
|
|
101
|
+
"wallet_address": wallet_address,
|
|
102
|
+
"chain_id": chain_id,
|
|
103
|
+
"token_address": token_address,
|
|
104
|
+
"token_amount": str(token_amount),
|
|
105
|
+
"usd_value": str(usd_value),
|
|
106
|
+
"data": data or {},
|
|
107
|
+
}
|
|
108
|
+
if strategy_name is not None:
|
|
109
|
+
payload["strategy_name"] = strategy_name
|
|
110
|
+
response = await self._authed_request("POST", url, json=payload)
|
|
111
|
+
data_resp = response.json()
|
|
112
|
+
return data_resp.get("data", data_resp)
|
|
113
|
+
|
|
114
|
+
async def add_vault_withdraw(
|
|
115
|
+
self,
|
|
116
|
+
*,
|
|
117
|
+
wallet_address: str,
|
|
118
|
+
chain_id: int,
|
|
119
|
+
token_address: str,
|
|
120
|
+
token_amount: str | float,
|
|
121
|
+
usd_value: str | float,
|
|
122
|
+
data: dict[str, Any] | None = None,
|
|
123
|
+
strategy_name: str | None = None,
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
Record a withdrawal for a vault.
|
|
127
|
+
|
|
128
|
+
POST /api/v1/public/vaults/withdrawals/
|
|
129
|
+
"""
|
|
130
|
+
url = f"{self.api_base_url}/public/vaults/withdrawals/"
|
|
131
|
+
payload: dict[str, Any] = {
|
|
132
|
+
"wallet_address": wallet_address,
|
|
133
|
+
"chain_id": chain_id,
|
|
134
|
+
"token_address": token_address,
|
|
135
|
+
"token_amount": str(token_amount),
|
|
136
|
+
"usd_value": str(usd_value),
|
|
137
|
+
"data": data or {},
|
|
138
|
+
}
|
|
139
|
+
if strategy_name is not None:
|
|
140
|
+
payload["strategy_name"] = strategy_name
|
|
141
|
+
response = await self._authed_request("POST", url, json=payload)
|
|
142
|
+
data_resp = response.json()
|
|
143
|
+
return data_resp.get("data", data_resp)
|
|
144
|
+
|
|
145
|
+
async def add_vault_operation(
|
|
146
|
+
self,
|
|
147
|
+
*,
|
|
148
|
+
wallet_address: str,
|
|
149
|
+
operation_data: dict[str, Any],
|
|
150
|
+
usd_value: str | float,
|
|
151
|
+
strategy_name: str | None = None,
|
|
152
|
+
) -> dict[str, Any]:
|
|
153
|
+
"""
|
|
154
|
+
Record a vault operation (e.g., swaps, rebalances) for bookkeeping.
|
|
155
|
+
|
|
156
|
+
POST /api/v1/public/vaults/operations/
|
|
157
|
+
"""
|
|
158
|
+
url = f"{self.api_base_url}/public/vaults/operations/"
|
|
159
|
+
payload: dict[str, Any] = {
|
|
160
|
+
"wallet_address": wallet_address,
|
|
161
|
+
"operation_data": operation_data,
|
|
162
|
+
"usd_value": str(usd_value),
|
|
163
|
+
}
|
|
164
|
+
if strategy_name is not None:
|
|
165
|
+
payload["strategy_name"] = strategy_name
|
|
166
|
+
response = await self._authed_request("POST", url, json=payload)
|
|
167
|
+
data_resp = response.json()
|
|
168
|
+
return data_resp.get("data", data_resp)
|
|
169
|
+
|
|
170
|
+
async def add_vault_cashflow(
|
|
171
|
+
self,
|
|
172
|
+
*,
|
|
173
|
+
wallet_address: str,
|
|
174
|
+
block_timestamp: int,
|
|
175
|
+
token_addr: str,
|
|
176
|
+
amount: str | int | float,
|
|
177
|
+
description: str,
|
|
178
|
+
strategy_name: str | None = None,
|
|
179
|
+
) -> dict[str, Any]:
|
|
180
|
+
"""
|
|
181
|
+
Record a cashflow for a vault (interest, funding, reward, or fee).
|
|
182
|
+
|
|
183
|
+
POST /api/v1/public/vaults/cashflows/
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
wallet_address: Vault wallet address
|
|
187
|
+
block_timestamp: Block timestamp (Unix timestamp)
|
|
188
|
+
token_addr: Token contract address
|
|
189
|
+
amount: Cashflow amount (in token units)
|
|
190
|
+
description: Cashflow type - must be one of: "interest", "funding", "reward", "fee", "lend", "unlend", "borrow"
|
|
191
|
+
strategy_name: Optional strategy name
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Dict containing the cashflow record or error details
|
|
195
|
+
"""
|
|
196
|
+
valid_descriptions = [
|
|
197
|
+
"interest",
|
|
198
|
+
"funding",
|
|
199
|
+
"reward",
|
|
200
|
+
"fee",
|
|
201
|
+
"lend",
|
|
202
|
+
"unlend",
|
|
203
|
+
"borrow",
|
|
204
|
+
]
|
|
205
|
+
if description not in valid_descriptions:
|
|
206
|
+
raise ValueError(
|
|
207
|
+
f"Invalid description '{description}'. Must be one of: {valid_descriptions}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
url = f"{self.api_base_url}/public/vaults/cashflows/"
|
|
211
|
+
payload: dict[str, Any] = {
|
|
212
|
+
"wallet_address": wallet_address,
|
|
213
|
+
"block_timestamp": block_timestamp,
|
|
214
|
+
"token_addr": token_addr,
|
|
215
|
+
"amount": str(amount),
|
|
216
|
+
"description": description,
|
|
217
|
+
}
|
|
218
|
+
if strategy_name is not None:
|
|
219
|
+
payload["strategy_name"] = strategy_name
|
|
220
|
+
response = await self._authed_request("POST", url, json=payload)
|
|
221
|
+
data_resp = response.json()
|
|
222
|
+
return data_resp.get("data", data_resp)
|