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.

Files changed (47) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
  4. wayfinder_paths/adapters/brap_adapter/README.md +11 -16
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
  6. wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
  7. wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
  9. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
  10. wayfinder_paths/adapters/pool_adapter/README.md +9 -10
  11. wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
  12. wayfinder_paths/adapters/token_adapter/README.md +2 -14
  13. wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
  14. wayfinder_paths/adapters/token_adapter/examples.json +4 -8
  15. wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
  16. wayfinder_paths/core/clients/BRAPClient.py +102 -61
  17. wayfinder_paths/core/clients/ClientManager.py +1 -68
  18. wayfinder_paths/core/clients/HyperlendClient.py +125 -64
  19. wayfinder_paths/core/clients/LedgerClient.py +1 -4
  20. wayfinder_paths/core/clients/PoolClient.py +122 -48
  21. wayfinder_paths/core/clients/TokenClient.py +91 -36
  22. wayfinder_paths/core/clients/WalletClient.py +26 -56
  23. wayfinder_paths/core/clients/WayfinderClient.py +28 -160
  24. wayfinder_paths/core/clients/__init__.py +0 -2
  25. wayfinder_paths/core/clients/protocols.py +35 -46
  26. wayfinder_paths/core/clients/sdk_example.py +37 -22
  27. wayfinder_paths/core/engine/StrategyJob.py +7 -55
  28. wayfinder_paths/core/services/local_evm_txn.py +6 -6
  29. wayfinder_paths/core/services/local_token_txn.py +1 -1
  30. wayfinder_paths/core/strategies/Strategy.py +0 -2
  31. wayfinder_paths/core/utils/evm_helpers.py +2 -2
  32. wayfinder_paths/run_strategy.py +8 -19
  33. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
  34. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
  35. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  36. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
  37. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
  38. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
  39. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
  40. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
  41. wayfinder_paths/templates/strategy/README.md +3 -3
  42. wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  43. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +14 -49
  44. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +46 -47
  45. wayfinder_paths/core/clients/AuthClient.py +0 -83
  46. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
  47. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/WHEEL +0 -0
@@ -1,5 +1,4 @@
1
1
  import json
2
- import os
3
2
  import time
4
3
  from typing import Any
5
4
 
@@ -11,13 +10,11 @@ from wayfinder_paths.core.constants.base import DEFAULT_HTTP_TIMEOUT
11
10
 
12
11
 
13
12
  class WayfinderClient:
14
- def __init__(self, api_key: str | None = None):
13
+ def __init__(self):
15
14
  """
16
15
  Initialize WayfinderClient.
17
16
 
18
- Args:
19
- api_key: Optional API key for service account authentication.
20
- If provided, uses API key auth. Otherwise falls back to config.json.
17
+ API key is loaded from system.api_key in config.json.
21
18
  """
22
19
  self.api_base_url = f"{get_api_base_url()}/"
23
20
  timeout = httpx.Timeout(DEFAULT_HTTP_TIMEOUT)
@@ -27,153 +24,51 @@ class WayfinderClient:
27
24
  "Content-Type": "application/json",
28
25
  }
29
26
 
30
- self._api_key: str | None = api_key
31
- self._access_token: str | None = None
32
- self._refresh_token: str | None = None
33
-
34
- def set_bearer_token(self, token: str) -> None:
35
- """
36
- Set runtime OAuth/JWT Bearer token for Wayfinder services.
37
- """
38
- self.headers["Authorization"] = f"Bearer {token}"
39
- self._access_token = token
40
-
41
- def set_tokens(self, access: str | None, refresh: str | None) -> None:
42
- """Set both access and refresh tokens and configure Authorization header."""
43
- if access:
44
- self.set_bearer_token(access)
45
- if refresh:
46
- self._refresh_token = refresh
47
-
48
27
  def clear_auth(self) -> None:
49
- """Clear Authorization headers (useful for logout or auth mode switch)."""
50
- self.headers.pop("Authorization", None)
51
-
52
- async def _refresh_access_token(self) -> bool:
53
- """Attempt to refresh access token using stored refresh token."""
54
- if not self._refresh_token:
55
- logger.debug("No refresh token available")
56
- return False
57
- try:
58
- logger.info("Attempting to refresh access token")
59
- start_time = time.time()
60
- url = f"{get_api_base_url()}/auth/token/refresh/"
61
- payload = {"refresh": self._refresh_token}
62
- response = await self.client.post(
63
- url, json=payload, headers={"Content-Type": "application/json"}
64
- )
65
- if response.status_code != 200:
66
- logger.warning(
67
- f"Token refresh failed with status {response.status_code}"
68
- )
69
- return False
70
- data = response.json()
71
- new_access = data.get("access") or data.get("access_token")
72
- if not new_access:
73
- logger.warning("No access token in refresh response")
74
- return False
75
- self.set_bearer_token(new_access)
76
- elapsed = time.time() - start_time
77
- logger.info(f"Access token refreshed successfully in {elapsed:.2f}s")
78
- return True
79
- except Exception as e:
80
- elapsed = time.time() - start_time
81
- logger.error(f"Token refresh failed after {elapsed:.2f}s: {e}")
82
- return False
28
+ """Clear X-API-KEY header."""
29
+ self.headers.pop("X-API-KEY", None)
83
30
 
84
31
  def _load_config_credentials(self) -> dict[str, str | None]:
85
32
  """
86
- Load credentials from config.json.
33
+ Load API key from config.json.
87
34
  Expected shape:
88
35
  {
89
- "user": { "username": ..., "password": ..., "refresh_token": ..., "api_key": ... },
90
36
  "system": { "api_key": ... }
91
37
  }
92
38
  """
93
39
  try:
94
40
  with open("config.json") as f:
95
41
  cfg = json.load(f)
96
- user = cfg.get("user", {}) if isinstance(cfg, dict) else {}
97
42
  system = cfg.get("system", {}) if isinstance(cfg, dict) else {}
98
- return {
99
- "username": user.get("username"),
100
- "password": user.get("password"),
101
- "refresh_token": user.get("refresh_token"),
102
- "api_key": user.get("api_key") or system.get("api_key"),
103
- }
43
+ api_key = system.get("api_key")
44
+ return {"api_key": api_key}
104
45
  except (FileNotFoundError, json.JSONDecodeError, OSError) as e:
105
46
  logger.debug(f"Could not load config file at config.json: {e}")
106
- return {
107
- "username": None,
108
- "password": None,
109
- "refresh_token": None,
110
- "api_key": None,
111
- }
47
+ return {"api_key": None}
112
48
  except Exception as e:
113
49
  logger.warning(f"Unexpected error loading config file at config.json: {e}")
114
- return {
115
- "username": None,
116
- "password": None,
117
- "refresh_token": None,
118
- "api_key": None,
119
- }
50
+ return {"api_key": None}
120
51
 
121
- async def _ensure_bearer_token(self) -> bool:
52
+ def _ensure_api_key(self) -> bool:
122
53
  """
123
- Ensure Authorization header is set. Priority: existing header > constructor api_key > config.json api_key > config.json tokens > username/password.
124
- Raises PermissionError if no credentials found.
54
+ Ensure X-API-KEY header is set from system.api_key in config.json.
55
+ Raises PermissionError if no API key found.
125
56
  """
126
- if self.headers.get("Authorization"):
57
+ if self.headers.get("X-API-KEY"):
127
58
  return True
128
59
 
129
- # Check for API key: constructor > config.json
130
- api_key = self._api_key
131
- if not api_key:
132
- creds = self._load_config_credentials()
133
- api_key = creds.get("api_key") or os.getenv("WAYFINDER_API_KEY")
60
+ creds = self._load_config_credentials()
61
+ api_key = creds.get("api_key")
134
62
 
135
63
  if api_key:
136
64
  api_key = api_key.strip() if isinstance(api_key, str) else api_key
137
65
  if not api_key:
138
66
  raise ValueError("API key cannot be empty")
139
- self.headers["Authorization"] = f"Bearer {api_key}"
67
+ self.headers["X-API-KEY"] = api_key
140
68
  return True
141
69
 
142
- # Fall back to OAuth token-based auth
143
- creds = self._load_config_credentials()
144
- refresh = creds.get("refresh_token")
145
-
146
- if refresh:
147
- self._refresh_token = refresh
148
- refreshed = await self._refresh_access_token()
149
- if refreshed:
150
- return True
151
-
152
- username = creds.get("username")
153
- password = creds.get("password")
154
-
155
- if username and password:
156
- try:
157
- url = f"{get_api_base_url()}/auth/token/"
158
- payload = {"username": username, "password": password}
159
- response = await self.client.post(
160
- url,
161
- json=payload,
162
- headers={"Content-Type": "application/json"},
163
- )
164
- if response.status_code == 200:
165
- data = response.json()
166
- access = data.get("access") or data.get("access_token")
167
- refresh = data.get("refresh") or data.get("refresh_token")
168
- self.set_tokens(access, refresh)
169
- return bool(access)
170
- except Exception as e:
171
- logger.debug(f"Failed to authenticate with username/password: {e}")
172
- pass
173
-
174
70
  raise PermissionError(
175
- "Not authenticated: provide api_key (via constructor or config.json) for service account auth, "
176
- "or username+password/refresh_token in config.json for personal access"
71
+ "Not authenticated: provide api_key in system.api_key in config.json"
177
72
  )
178
73
 
179
74
  async def _request(
@@ -182,49 +77,32 @@ class WayfinderClient:
182
77
  url: str,
183
78
  *,
184
79
  headers: dict[str, str] | None = None,
185
- retry_on_401: bool = True,
80
+ retry_on_401: bool = False,
186
81
  **kwargs: Any,
187
82
  ) -> httpx.Response:
188
83
  """
189
- Wrapper around httpx that injects headers and auto-refreshes tokens on 401 once.
190
- Ensures API key or bearer token is set in headers when available (for service account auth and rate limits).
84
+ Wrapper around httpx that injects X-API-KEY header.
85
+ Ensures API key is set in headers when available (for authentication and rate limiting).
191
86
  """
192
87
  logger.debug(f"Making {method} request to {url}")
193
88
  start_time = time.time()
194
89
 
195
- # Ensure API key or bearer token is set in headers if available and not already set
90
+ # Ensure API key is set in headers if available and not already set
196
91
  # This ensures API keys are passed to all endpoints (including public ones) for rate limiting
197
- if not self.headers.get("Authorization"):
198
- # Try to get API key from constructor or config
199
- api_key = self._api_key
200
- if not api_key:
201
- creds = self._load_config_credentials()
202
- api_key = creds.get("api_key") or os.getenv("WAYFINDER_API_KEY")
92
+ if not self.headers.get("X-API-KEY"):
93
+ creds = self._load_config_credentials()
94
+ api_key = creds.get("api_key")
203
95
 
204
96
  if api_key:
205
97
  api_key = api_key.strip() if isinstance(api_key, str) else api_key
206
98
  if api_key:
207
- self.headers["Authorization"] = f"Bearer {api_key}"
99
+ self.headers["X-API-KEY"] = api_key
208
100
 
209
101
  merged_headers = dict(self.headers)
210
102
  if headers:
211
103
  merged_headers.update(headers)
212
104
  resp = await self.client.request(method, url, headers=merged_headers, **kwargs)
213
105
 
214
- if resp.status_code == 401 and retry_on_401 and self._refresh_token:
215
- logger.info("Received 401, attempting token refresh and retry")
216
- refreshed = await self._refresh_access_token()
217
- if refreshed:
218
- merged_headers = dict(self.headers)
219
- if headers:
220
- merged_headers.update(headers)
221
- resp = await self.client.request(
222
- method, url, headers=merged_headers, **kwargs
223
- )
224
- logger.info("Retry after token refresh successful")
225
- else:
226
- logger.error("Token refresh failed, request will fail")
227
-
228
106
  elapsed = time.time() - start_time
229
107
  if resp.status_code >= 400:
230
108
  logger.warning(
@@ -247,17 +125,7 @@ class WayfinderClient:
247
125
  **kwargs: Any,
248
126
  ) -> httpx.Response:
249
127
  """
250
- Ensure Authorization (via env/config creds) and perform the request.
251
- Retries once on 401 by re-acquiring tokens.
128
+ Ensure X-API-KEY header is set (from system.api_key in config.json) and perform the request.
252
129
  """
253
- ok = await self._ensure_bearer_token()
254
- if not ok:
255
- raise PermissionError("Not authenticated: set env tokens or credentials")
256
- try:
257
- return await self._request(method, url, headers=headers, **kwargs)
258
- except httpx.HTTPStatusError as e:
259
- if e.response is not None and e.response.status_code == 401:
260
- # Retry after attempting re-acquire/refresh
261
- await self._ensure_bearer_token()
262
- return await self._request(method, url, headers=headers, **kwargs)
263
- raise
130
+ self._ensure_api_key()
131
+ return await self._request(method, url, headers=headers, **kwargs)
@@ -2,7 +2,6 @@
2
2
  Core client modules for API communication
3
3
  """
4
4
 
5
- from wayfinder_paths.core.clients.AuthClient import AuthClient
6
5
  from wayfinder_paths.core.clients.BRAPClient import BRAPClient
7
6
  from wayfinder_paths.core.clients.ClientManager import ClientManager
8
7
  from wayfinder_paths.core.clients.HyperlendClient import HyperlendClient
@@ -23,7 +22,6 @@ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
23
22
  __all__ = [
24
23
  "WayfinderClient",
25
24
  "ClientManager",
26
- "AuthClient",
27
25
  "TokenClient",
28
26
  "WalletClient",
29
27
  "LedgerClient",
@@ -4,7 +4,7 @@ Protocol definitions for API clients.
4
4
  These protocols define the interface that all client implementations must satisfy.
5
5
  When used as an SDK, users can provide custom implementations that match these protocols.
6
6
 
7
- Note: AuthClient is excluded as SDK users handle their own authentication.
7
+ Note: Authentication is handled via X-API-KEY header in WayfinderClient base class.
8
8
  """
9
9
 
10
10
  from __future__ import annotations
@@ -12,19 +12,19 @@ from __future__ import annotations
12
12
  from typing import TYPE_CHECKING, Any, Protocol
13
13
 
14
14
  if TYPE_CHECKING:
15
- from wayfinder_paths.core.clients.BRAPClient import BRAPQuote
15
+ from wayfinder_paths.core.clients.BRAPClient import BRAPQuoteResponse
16
16
  from wayfinder_paths.core.clients.HyperlendClient import (
17
17
  AssetsView,
18
18
  LendRateHistory,
19
19
  MarketEntry,
20
- StableMarket,
20
+ StableMarketsHeadroomResponse,
21
21
  )
22
22
  from wayfinder_paths.core.clients.LedgerClient import (
23
23
  StrategyTransactionList,
24
24
  TransactionRecord,
25
25
  )
26
26
  from wayfinder_paths.core.clients.PoolClient import (
27
- LlamaMatch,
27
+ LlamaMatchesResponse,
28
28
  PoolList,
29
29
  )
30
30
  from wayfinder_paths.core.clients.TokenClient import (
@@ -32,8 +32,7 @@ if TYPE_CHECKING:
32
32
  TokenDetails,
33
33
  )
34
34
  from wayfinder_paths.core.clients.WalletClient import (
35
- PoolBalance,
36
- TokenBalance,
35
+ AddressBalance,
37
36
  )
38
37
 
39
38
 
@@ -41,7 +40,10 @@ class TokenClientProtocol(Protocol):
41
40
  """Protocol for token-related operations"""
42
41
 
43
42
  async def get_token_details(
44
- self, token_id: str, force_refresh: bool = False
43
+ self,
44
+ query: str,
45
+ market_data: bool = True,
46
+ chain_id: int | None = None,
45
47
  ) -> TokenDetails:
46
48
  """Get token data including price from the token-details endpoint"""
47
49
  ...
@@ -57,19 +59,16 @@ class HyperlendClientProtocol(Protocol):
57
59
  async def get_stable_markets(
58
60
  self,
59
61
  *,
60
- chain_id: int,
61
62
  required_underlying_tokens: float | None = None,
62
63
  buffer_bps: int | None = None,
63
64
  min_buffer_tokens: float | None = None,
64
- is_stable_symbol: bool | None = None,
65
- ) -> list[StableMarket]:
66
- """Fetch stable markets from Hyperlend"""
65
+ ) -> StableMarketsHeadroomResponse:
66
+ """Fetch stable markets headroom from Hyperlend"""
67
67
  ...
68
68
 
69
69
  async def get_assets_view(
70
70
  self,
71
71
  *,
72
- chain_id: int,
73
72
  user_address: str,
74
73
  ) -> AssetsView:
75
74
  """Fetch assets view for a user address from Hyperlend"""
@@ -78,8 +77,7 @@ class HyperlendClientProtocol(Protocol):
78
77
  async def get_market_entry(
79
78
  self,
80
79
  *,
81
- chain_id: int,
82
- token_address: str,
80
+ token: str,
83
81
  ) -> MarketEntry:
84
82
  """Fetch market entry from Hyperlend"""
85
83
  ...
@@ -87,9 +85,9 @@ class HyperlendClientProtocol(Protocol):
87
85
  async def get_lend_rate_history(
88
86
  self,
89
87
  *,
90
- chain_id: int,
91
- token_address: str,
88
+ token: str,
92
89
  lookback_hours: int,
90
+ force_refresh: bool | None = None,
93
91
  ) -> LendRateHistory:
94
92
  """Fetch lend rate history from Hyperlend"""
95
93
  ...
@@ -161,25 +159,14 @@ class LedgerClientProtocol(Protocol):
161
159
  class WalletClientProtocol(Protocol):
162
160
  """Protocol for wallet-related operations"""
163
161
 
164
- async def get_token_balance_for_wallet(
162
+ async def get_token_balance_for_address(
165
163
  self,
166
164
  *,
167
- token_id: str,
168
165
  wallet_address: str,
169
- human_readable: bool = True,
170
- ) -> TokenBalance:
171
- """Fetch a single token balance for an explicit wallet address"""
172
- ...
173
-
174
- async def get_pool_balance_for_wallet(
175
- self,
176
- *,
177
- pool_address: str,
178
- chain_id: int,
179
- user_address: str,
180
- human_readable: bool = True,
181
- ) -> PoolBalance:
182
- """Fetch a wallet's LP/share balance for a given pool address and chain"""
166
+ query: str,
167
+ chain_id: int | None = None,
168
+ ) -> AddressBalance:
169
+ """Fetch a balance for an address + chain + query (supports compound query formats)"""
183
170
  ...
184
171
 
185
172
 
@@ -189,13 +176,18 @@ class PoolClientProtocol(Protocol):
189
176
  async def get_pools_by_ids(
190
177
  self,
191
178
  *,
192
- pool_ids: str,
179
+ pool_ids: list[str] | str,
193
180
  ) -> PoolList:
194
- """Fetch pools by comma-separated pool ids"""
181
+ """Fetch pools by pool IDs (list or comma-separated string)"""
195
182
  ...
196
183
 
197
- async def get_pools(self) -> dict[str, LlamaMatch]:
198
- """Fetch Llama matches for pools"""
184
+ async def get_pools(
185
+ self,
186
+ *,
187
+ chain_id: int | None = None,
188
+ project: str | None = None,
189
+ ) -> LlamaMatchesResponse:
190
+ """Fetch pools (optionally filtered by chain_id and project)"""
199
191
  ...
200
192
 
201
193
 
@@ -205,16 +197,13 @@ class BRAPClientProtocol(Protocol):
205
197
  async def get_quote(
206
198
  self,
207
199
  *,
208
- from_token_address: str,
209
- to_token_address: str,
210
- from_chain_id: int,
211
- to_chain_id: int,
212
- from_address: str,
213
- to_address: str,
214
- amount1: str,
215
- slippage: float | None = None,
216
- wayfinder_fee: float | None = None,
217
- ) -> BRAPQuote:
200
+ from_token: str,
201
+ to_token: str,
202
+ from_chain: int,
203
+ to_chain: int,
204
+ from_wallet: str,
205
+ from_amount: str,
206
+ ) -> BRAPQuoteResponse:
218
207
  """Get a quote for a bridge/swap operation"""
219
208
  ...
220
209
 
@@ -38,59 +38,74 @@ class MockHyperlendClient:
38
38
  async def get_stable_markets(
39
39
  self,
40
40
  *,
41
- chain_id: int,
42
41
  required_underlying_tokens: float | None = None,
43
42
  buffer_bps: int | None = None,
44
43
  min_buffer_tokens: float | None = None,
45
- is_stable_symbol: bool | None = None,
46
44
  ) -> dict[str, Any]:
47
45
  return {
48
- "markets": [
49
- {
50
- "chain_id": chain_id,
51
- "token_address": "0xMockToken",
46
+ "markets": {
47
+ "0xMockToken": {
52
48
  "symbol": "USDC",
53
- "lend_rate": 0.05,
54
- "available_liquidity": 1000000.0,
49
+ "symbol_canonical": "usdc",
50
+ "display_symbol": "USDC",
51
+ "reserve": {},
52
+ "decimals": 6,
53
+ "headroom": 1000000000000,
54
+ "supply_cap": 5000000000000,
55
55
  }
56
- ]
56
+ },
57
+ "notes": [],
57
58
  }
58
59
 
59
60
  async def get_assets_view(
60
61
  self,
61
62
  *,
62
- chain_id: int,
63
63
  user_address: str,
64
64
  ) -> dict[str, Any]:
65
65
  return {
66
- "user_address": user_address,
67
- "chain_id": chain_id,
66
+ "block_number": 12345,
67
+ "user": user_address,
68
+ "native_balance_wei": 0,
69
+ "native_balance": 0.0,
68
70
  "assets": [],
71
+ "account_data": {
72
+ "total_collateral_base": 0,
73
+ "total_debt_base": 0,
74
+ "available_borrows_base": 0,
75
+ "current_liquidation_threshold": 0,
76
+ "ltv": 0,
77
+ "health_factor_wad": 0,
78
+ "health_factor": 0.0,
79
+ },
80
+ "base_currency_info": {
81
+ "marketReferenceCurrencyUnit": 100000000,
82
+ "marketReferenceCurrencyPriceInUsd": 100000000,
83
+ "networkBaseTokenPriceInUsd": 0,
84
+ "networkBaseTokenPriceDecimals": 8,
85
+ },
69
86
  }
70
87
 
71
88
  async def get_market_entry(
72
89
  self,
73
90
  *,
74
- chain_id: int,
75
- token_address: str,
91
+ token: str,
76
92
  ) -> dict[str, Any]:
77
93
  return {
78
- "chain_id": chain_id,
79
- "token_address": token_address,
80
- "market_data": {},
94
+ "symbol": "USDC",
95
+ "symbol_canonical": "usdc",
96
+ "display_symbol": "USDC",
97
+ "reserve": {},
81
98
  }
82
99
 
83
100
  async def get_lend_rate_history(
84
101
  self,
85
102
  *,
86
- chain_id: int,
87
- token_address: str,
103
+ token: str,
88
104
  lookback_hours: int,
105
+ force_refresh: bool | None = None,
89
106
  ) -> dict[str, Any]:
90
107
  return {
91
- "chain_id": chain_id,
92
- "token_address": token_address,
93
- "rates": [],
108
+ "history": [],
94
109
  }
95
110
 
96
111
 
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import os
3
2
  from typing import Any
4
3
 
5
4
  from loguru import logger
@@ -16,7 +15,6 @@ class StrategyJob:
16
15
  config: StrategyJobConfig,
17
16
  clients: dict[str, Any] | None = None,
18
17
  skip_auth: bool = False,
19
- api_key: str | None = None,
20
18
  ):
21
19
  """
22
20
  Initialize a StrategyJob.
@@ -26,16 +24,12 @@ class StrategyJob:
26
24
  config: Strategy job configuration.
27
25
  clients: Optional dict of pre-instantiated clients to inject directly.
28
26
  skip_auth: If True, skips authentication (for SDK usage).
29
- api_key: Optional API key for service account authentication.
30
- If provided, will be passed to ClientManager and strategy.
31
27
  """
32
28
  self.strategy = strategy
33
29
  self.config = config
34
30
 
35
31
  self.job_id = strategy.name or "unknown"
36
- self.clients = ClientManager(
37
- clients=clients, skip_auth=skip_auth, api_key=api_key
38
- )
32
+ self.clients = ClientManager(clients=clients, skip_auth=skip_auth)
39
33
 
40
34
  def _setup_strategy(self):
41
35
  """Setup the strategy instance"""
@@ -44,23 +38,6 @@ class StrategyJob:
44
38
 
45
39
  self.strategy.log = self.log
46
40
 
47
- def _is_using_api_key(self) -> bool:
48
- """Check if API key authentication is being used."""
49
- if self.clients._api_key:
50
- return True
51
-
52
- if self.clients.auth:
53
- try:
54
- creds = self.clients.auth._load_config_credentials()
55
- if creds.get("api_key"):
56
- return True
57
- if os.getenv("WAYFINDER_API_KEY"):
58
- return True
59
- except Exception:
60
- pass
61
-
62
- return False
63
-
64
41
  async def setup(self):
65
42
  """
66
43
  Initialize the strategy job and strategy.
@@ -69,38 +46,13 @@ class StrategyJob:
69
46
  """
70
47
  self._setup_strategy()
71
48
 
72
- # Ensure auth token is set for API calls
49
+ # Ensure API key is set for API calls
50
+ # All clients inherit from WayfinderClient and have _ensure_api_key()
73
51
  if not self.clients._skip_auth:
74
- is_api_key_auth = self._is_using_api_key()
75
-
76
- if is_api_key_auth:
77
- logger.debug("Using API key authentication")
78
- if self.clients.auth:
79
- await self.clients.auth._ensure_bearer_token()
80
- else:
81
- # Try to ensure bearer token is set, authenticate if needed
82
- try:
83
- if self.clients.auth:
84
- await self.clients.auth._ensure_bearer_token()
85
- except (PermissionError, Exception) as e:
86
- if not isinstance(e, PermissionError):
87
- logger.warning(
88
- f"Authentication failed: {e}, trying OAuth fallback"
89
- )
90
- username = self.config.user.username
91
- password = self.config.user.password
92
- refresh_token = self.config.user.refresh_token
93
- if refresh_token or (username and password):
94
- await self.clients.authenticate(
95
- username=username,
96
- password=password,
97
- refresh_token=refresh_token,
98
- )
99
- else:
100
- raise ValueError(
101
- "Authentication required: provide api_key parameter for service account auth, "
102
- "or username+password/refresh_token in config.json for personal access"
103
- ) from e
52
+ # Ensure API key on any client (they all share the same method)
53
+ token_client = self.clients.token
54
+ if token_client:
55
+ token_client._ensure_api_key()
104
56
 
105
57
  existing_cfg = dict(getattr(self.strategy, "config", {}) or {})
106
58
  strategy_cfg = dict(self.config.strategy_config or {})
@@ -155,13 +155,13 @@ class LocalEvmTxn(EvmTxn):
155
155
  gas_price = await self._get_gas_price(w3)
156
156
 
157
157
  transaction["gasPrice"] = int(gas_price * SUGGESTED_GAS_PRICE_MULTIPLIER)
158
- elif chain_id == 999:
159
- big_block_gas_price = await w3.hype.big_block_gas_price()
158
+ # elif chain_id == 999:
159
+ # big_block_gas_price = await w3.hype.big_block_gas_price()
160
160
 
161
- transaction["maxFeePerGas"] = int(
162
- big_block_gas_price * SUGGESTED_PRIORITY_FEE_MULTIPLIER
163
- )
164
- transaction["maxPriorityFeePerGas"] = 0
161
+ # transaction["maxFeePerGas"] = int(
162
+ # big_block_gas_price * SUGGESTED_PRIORITY_FEE_MULTIPLIER
163
+ # )
164
+ # transaction["maxPriorityFeePerGas"] = 0
165
165
  else:
166
166
  base_fee = await self._get_base_fee(w3)
167
167
  priority_fee = await self._get_priority_fee(w3)