wayfinder-paths 0.1.4__py3-none-any.whl → 0.1.6__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 (61) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +14 -14
  2. wayfinder_paths/__init__.py +4 -3
  3. wayfinder_paths/adapters/balance_adapter/README.md +10 -10
  4. wayfinder_paths/adapters/balance_adapter/adapter.py +10 -9
  5. wayfinder_paths/adapters/balance_adapter/examples.json +1 -1
  6. wayfinder_paths/adapters/brap_adapter/README.md +1 -1
  7. wayfinder_paths/adapters/brap_adapter/adapter.py +28 -21
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +33 -26
  9. wayfinder_paths/adapters/ledger_adapter/README.md +26 -39
  10. wayfinder_paths/adapters/ledger_adapter/adapter.py +78 -75
  11. wayfinder_paths/adapters/ledger_adapter/examples.json +10 -4
  12. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +4 -4
  13. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +31 -26
  14. wayfinder_paths/adapters/pool_adapter/README.md +1 -13
  15. wayfinder_paths/adapters/pool_adapter/adapter.py +12 -19
  16. wayfinder_paths/adapters/token_adapter/adapter.py +8 -4
  17. wayfinder_paths/core/__init__.py +9 -4
  18. wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
  19. wayfinder_paths/core/adapters/models.py +41 -0
  20. wayfinder_paths/core/clients/BRAPClient.py +21 -2
  21. wayfinder_paths/core/clients/ClientManager.py +42 -63
  22. wayfinder_paths/core/clients/HyperlendClient.py +46 -5
  23. wayfinder_paths/core/clients/LedgerClient.py +350 -124
  24. wayfinder_paths/core/clients/PoolClient.py +51 -19
  25. wayfinder_paths/core/clients/SimulationClient.py +16 -4
  26. wayfinder_paths/core/clients/TokenClient.py +34 -18
  27. wayfinder_paths/core/clients/TransactionClient.py +18 -2
  28. wayfinder_paths/core/clients/WalletClient.py +35 -4
  29. wayfinder_paths/core/clients/WayfinderClient.py +16 -5
  30. wayfinder_paths/core/clients/protocols.py +69 -62
  31. wayfinder_paths/core/clients/sdk_example.py +0 -5
  32. wayfinder_paths/core/config.py +192 -103
  33. wayfinder_paths/core/constants/base.py +17 -0
  34. wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
  35. wayfinder_paths/core/engine/__init__.py +2 -2
  36. wayfinder_paths/core/services/base.py +6 -4
  37. wayfinder_paths/core/services/local_evm_txn.py +3 -2
  38. wayfinder_paths/core/settings.py +2 -2
  39. wayfinder_paths/core/strategies/Strategy.py +135 -38
  40. wayfinder_paths/core/strategies/descriptors.py +1 -0
  41. wayfinder_paths/core/utils/evm_helpers.py +12 -10
  42. wayfinder_paths/core/wallets/README.md +3 -3
  43. wayfinder_paths/core/wallets/WalletManager.py +3 -3
  44. wayfinder_paths/run_strategy.py +26 -24
  45. wayfinder_paths/scripts/make_wallets.py +6 -6
  46. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +6 -6
  47. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +36 -156
  48. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +6 -6
  49. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +11 -11
  50. wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +1 -1
  51. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +92 -92
  52. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +6 -6
  53. wayfinder_paths/templates/adapter/README.md +1 -1
  54. wayfinder_paths/templates/adapter/test_adapter.py +1 -1
  55. wayfinder_paths/templates/strategy/README.md +4 -4
  56. wayfinder_paths/templates/strategy/test_strategy.py +7 -7
  57. wayfinder_paths/tests/test_test_coverage.py +5 -5
  58. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/METADATA +46 -47
  59. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/RECORD +61 -60
  60. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/LICENSE +0 -0
  61. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/WHEEL +0 -0
@@ -3,14 +3,26 @@ Simulation Client
3
3
  Handles blockchain transaction simulations via Gorlami/Tenderly
4
4
  """
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  import time
7
- from typing import Any
9
+ from typing import NotRequired, Required, TypedDict
8
10
 
9
11
  from loguru import logger
10
12
 
11
13
  from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
12
14
 
13
15
 
16
+ class SimulationResult(TypedDict):
17
+ """Simulation result structure"""
18
+
19
+ success: Required[bool]
20
+ gas_used: NotRequired[str | None]
21
+ gas_price: NotRequired[str | None]
22
+ balances: NotRequired[dict[str, str]]
23
+ error: NotRequired[str | None]
24
+
25
+
14
26
  class SimulationClient(WayfinderClient):
15
27
  """Client for blockchain transaction simulations"""
16
28
 
@@ -25,7 +37,7 @@ class SimulationClient(WayfinderClient):
25
37
  amount: str,
26
38
  chain_id: int,
27
39
  initial_balances: dict[str, str],
28
- ) -> dict[str, Any]:
40
+ ) -> SimulationResult:
29
41
  """
30
42
  Simulate sending native ETH or ERC20 tokens.
31
43
 
@@ -77,7 +89,7 @@ class SimulationClient(WayfinderClient):
77
89
  chain_id: int,
78
90
  initial_balances: dict[str, str],
79
91
  clear_approval_first: bool = False,
80
- ) -> dict[str, Any]:
92
+ ) -> SimulationResult:
81
93
  """
82
94
  Simulate ERC20 token approval.
83
95
 
@@ -132,7 +144,7 @@ class SimulationClient(WayfinderClient):
132
144
  from_address: str,
133
145
  slippage: float,
134
146
  initial_balances: dict[str, str],
135
- ) -> dict[str, Any]:
147
+ ) -> SimulationResult:
136
148
  """
137
149
  Simulate token swap operation.
138
150
 
@@ -3,13 +3,44 @@ Token Adapter
3
3
  Handles token information, prices, and parsing
4
4
  """
5
5
 
6
- from typing import Any
6
+ from __future__ import annotations
7
+
8
+ from typing import NotRequired, Required, TypedDict
7
9
 
8
10
  from wayfinder_paths.core.clients.AuthClient import AuthClient
9
11
  from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
12
  from wayfinder_paths.core.settings import settings
11
13
 
12
14
 
15
+ class TokenDetails(TypedDict):
16
+ """Token details response structure"""
17
+
18
+ id: Required[str]
19
+ address: Required[str]
20
+ symbol: Required[str]
21
+ name: Required[str]
22
+ decimals: Required[int]
23
+ chain_id: Required[int]
24
+ chain_code: Required[str]
25
+ price_usd: NotRequired[float]
26
+ price: NotRequired[float]
27
+ image_url: NotRequired[str | None]
28
+ coingecko_id: NotRequired[str | None]
29
+
30
+
31
+ class GasToken(TypedDict):
32
+ """Gas token response structure"""
33
+
34
+ id: Required[str]
35
+ address: Required[str]
36
+ symbol: Required[str]
37
+ name: Required[str]
38
+ decimals: Required[int]
39
+ chain_id: Required[int]
40
+ chain_code: Required[str]
41
+ price_usd: NotRequired[float]
42
+
43
+
13
44
  class TokenClient(WayfinderClient):
14
45
  """Adapter for token-related operations"""
15
46
 
@@ -22,7 +53,7 @@ class TokenClient(WayfinderClient):
22
53
 
23
54
  async def get_token_details(
24
55
  self, token_id: str, force_refresh: bool = False
25
- ) -> dict[str, Any]:
56
+ ) -> TokenDetails:
26
57
  """
27
58
  Get token data including price from the token-details endpoint
28
59
 
@@ -44,7 +75,7 @@ class TokenClient(WayfinderClient):
44
75
  data = response.json()
45
76
  return data.get("data", data)
46
77
 
47
- async def get_gas_token(self, chain_code: str) -> dict[str, Any]:
78
+ async def get_gas_token(self, chain_code: str) -> GasToken:
48
79
  """
49
80
  Fetch the native gas token for a given chain code via public endpoint.
50
81
  Example: GET /api/v1/public/tokens/gas/?chain_code=base
@@ -56,18 +87,3 @@ class TokenClient(WayfinderClient):
56
87
  response.raise_for_status()
57
88
  data = response.json()
58
89
  return data.get("data", data)
59
-
60
- async def is_native_token(
61
- self, token_address: str, chain_id: int
62
- ) -> dict[str, Any]:
63
- """
64
- Determine if a token address corresponds to the native gas token on a chain.
65
- Returns the API payload (usually includes an `is_native` boolean).
66
- """
67
- url = f"{settings.WAYFINDER_API_URL}/public/tokens/is-native/"
68
- params = {"token_address": token_address, "chain_id": str(chain_id)}
69
- # Public endpoint: do not pass auth headers
70
- response = await self._request("GET", url, params=params, headers={})
71
- response.raise_for_status()
72
- data = response.json()
73
- return data.get("data", data)
@@ -3,13 +3,29 @@ Transaction Client
3
3
  Handles transaction building, gas estimation, and monitoring
4
4
  """
5
5
 
6
- from typing import Any
6
+ from __future__ import annotations
7
+
8
+ from typing import NotRequired, Required, TypedDict
7
9
 
8
10
  from wayfinder_paths.core.clients.AuthClient import AuthClient
9
11
  from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
12
  from wayfinder_paths.core.settings import settings
11
13
 
12
14
 
15
+ class TransactionPayload(TypedDict):
16
+ """Transaction payload structure"""
17
+
18
+ from_address: Required[str]
19
+ to_address: Required[str]
20
+ token_address: Required[str]
21
+ amount: Required[str]
22
+ chain_id: Required[int]
23
+ data: NotRequired[str | None]
24
+ value: NotRequired[str | None]
25
+ gas_limit: NotRequired[str | None]
26
+ gas_price: NotRequired[str | None]
27
+
28
+
13
29
  class TransactionClient(WayfinderClient):
14
30
  """Client for transaction operations"""
15
31
 
@@ -27,7 +43,7 @@ class TransactionClient(WayfinderClient):
27
43
  token_address: str,
28
44
  amount: float,
29
45
  chain_id: int,
30
- ) -> dict[str, Any]:
46
+ ) -> TransactionPayload:
31
47
  """
32
48
  Build a send transaction payload for EVM tokens/native transfers.
33
49
 
@@ -3,13 +3,44 @@ Wallet Client
3
3
  Fetches wallet-related data such as aggregated balances for the authenticated user.
4
4
  """
5
5
 
6
- from typing import Any
6
+ from __future__ import annotations
7
+
8
+ from typing import NotRequired, Required, TypedDict
7
9
 
8
10
  from wayfinder_paths.core.clients.AuthClient import AuthClient
9
11
  from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
12
  from wayfinder_paths.core.settings import settings
11
13
 
12
14
 
15
+ class TokenBalance(TypedDict):
16
+ """Token balance response structure"""
17
+
18
+ token_id: Required[str]
19
+ wallet_address: Required[str]
20
+ balance: Required[str]
21
+ balance_human: NotRequired[float | None]
22
+ usd_value: NotRequired[float | None]
23
+
24
+
25
+ class PoolBalance(TypedDict):
26
+ """Pool balance response structure"""
27
+
28
+ pool_address: Required[str]
29
+ chain_id: Required[int]
30
+ user_address: Required[str]
31
+ balance: Required[str]
32
+ balance_human: NotRequired[float | None]
33
+ usd_value: NotRequired[float | None]
34
+
35
+
36
+ class EnrichedBalances(TypedDict):
37
+ """Enriched token balances response structure"""
38
+
39
+ wallet_address: Required[str]
40
+ balances: Required[list[TokenBalance]]
41
+ total_usd_value: NotRequired[float | None]
42
+
43
+
13
44
  class WalletClient(WayfinderClient):
14
45
  def __init__(self, api_key: str | None = None):
15
46
  super().__init__(api_key=api_key)
@@ -22,7 +53,7 @@ class WalletClient(WayfinderClient):
22
53
  token_id: str,
23
54
  wallet_address: str,
24
55
  human_readable: bool = True,
25
- ) -> dict[str, Any]:
56
+ ) -> TokenBalance:
26
57
  """
27
58
  Fetch a single token balance for an explicit wallet address.
28
59
 
@@ -45,7 +76,7 @@ class WalletClient(WayfinderClient):
45
76
  chain_id: int,
46
77
  user_address: str,
47
78
  human_readable: bool = True,
48
- ) -> dict[str, Any]:
79
+ ) -> PoolBalance:
49
80
  """
50
81
  Fetch a wallet's LP/share balance for a given pool address and chain.
51
82
 
@@ -69,7 +100,7 @@ class WalletClient(WayfinderClient):
69
100
  enrich: bool = True,
70
101
  from_cache: bool = False,
71
102
  add_llama: bool = True,
72
- ) -> dict[str, Any]:
103
+ ) -> EnrichedBalances:
73
104
  """
74
105
  Fetch all token balances for a wallet with enrichment via the enriched endpoint.
75
106
 
@@ -6,6 +6,7 @@ from typing import Any
6
6
  import httpx
7
7
  from loguru import logger
8
8
 
9
+ from wayfinder_paths.core.constants.base import DEFAULT_HTTP_TIMEOUT
9
10
  from wayfinder_paths.core.settings import settings
10
11
 
11
12
 
@@ -19,7 +20,7 @@ class WayfinderClient:
19
20
  If provided, uses API key auth. Otherwise falls back to config.json.
20
21
  """
21
22
  self.api_base_url = f"{settings.WAYFINDER_API_URL}/"
22
- timeout = httpx.Timeout(30.0)
23
+ timeout = httpx.Timeout(DEFAULT_HTTP_TIMEOUT)
23
24
  self.client = httpx.AsyncClient(timeout=timeout)
24
25
 
25
26
  self.headers = {
@@ -101,7 +102,16 @@ class WayfinderClient:
101
102
  "refresh_token": user.get("refresh_token"),
102
103
  "api_key": user.get("api_key") or system.get("api_key"),
103
104
  }
104
- except Exception:
105
+ except (FileNotFoundError, json.JSONDecodeError, OSError) as e:
106
+ logger.debug(f"Could not load config file at {path}: {e}")
107
+ return {
108
+ "username": None,
109
+ "password": None,
110
+ "refresh_token": None,
111
+ "api_key": None,
112
+ }
113
+ except Exception as e:
114
+ logger.warning(f"Unexpected error loading config file at {path}: {e}")
105
115
  return {
106
116
  "username": None,
107
117
  "password": None,
@@ -161,9 +171,10 @@ class WayfinderClient:
161
171
  data = response.json()
162
172
  access = data.get("access") or data.get("access_token")
163
173
  refresh = data.get("refresh") or data.get("refresh_token")
164
- self.set_tokens(access, refresh)
165
- return bool(access)
166
- except Exception:
174
+ self.set_tokens(access, refresh)
175
+ return bool(access)
176
+ except Exception as e:
177
+ logger.debug(f"Failed to authenticate with username/password: {e}")
167
178
  pass
168
179
 
169
180
  raise PermissionError(
@@ -7,7 +7,39 @@ When used as an SDK, users can provide custom implementations that match these p
7
7
  Note: AuthClient is excluded as SDK users handle their own authentication.
8
8
  """
9
9
 
10
- from typing import Any, Protocol
10
+ from __future__ import annotations
11
+
12
+ from typing import TYPE_CHECKING, Any, Protocol
13
+
14
+ if TYPE_CHECKING:
15
+ from wayfinder_paths.core.clients.BRAPClient import BRAPQuote
16
+ from wayfinder_paths.core.clients.HyperlendClient import (
17
+ AssetsView,
18
+ LendRateHistory,
19
+ MarketEntry,
20
+ StableMarket,
21
+ )
22
+ from wayfinder_paths.core.clients.LedgerClient import (
23
+ NetDeposit,
24
+ StrategyTransactionList,
25
+ TransactionRecord,
26
+ )
27
+ from wayfinder_paths.core.clients.PoolClient import (
28
+ LlamaMatch,
29
+ LlamaReport,
30
+ PoolList,
31
+ )
32
+ from wayfinder_paths.core.clients.SimulationClient import SimulationResult
33
+ from wayfinder_paths.core.clients.TokenClient import (
34
+ GasToken,
35
+ TokenDetails,
36
+ )
37
+ from wayfinder_paths.core.clients.TransactionClient import TransactionPayload
38
+ from wayfinder_paths.core.clients.WalletClient import (
39
+ EnrichedBalances,
40
+ PoolBalance,
41
+ TokenBalance,
42
+ )
11
43
 
12
44
 
13
45
  class TokenClientProtocol(Protocol):
@@ -15,20 +47,14 @@ class TokenClientProtocol(Protocol):
15
47
 
16
48
  async def get_token_details(
17
49
  self, token_id: str, force_refresh: bool = False
18
- ) -> dict[str, Any]:
50
+ ) -> TokenDetails:
19
51
  """Get token data including price from the token-details endpoint"""
20
52
  ...
21
53
 
22
- async def get_gas_token(self, chain_code: str) -> dict[str, Any]:
54
+ async def get_gas_token(self, chain_code: str) -> GasToken:
23
55
  """Fetch the native gas token for a given chain code"""
24
56
  ...
25
57
 
26
- async def is_native_token(
27
- self, token_address: str, chain_id: int
28
- ) -> dict[str, Any]:
29
- """Determine if a token address corresponds to the native gas token on a chain"""
30
- ...
31
-
32
58
 
33
59
  class HyperlendClientProtocol(Protocol):
34
60
  """Protocol for Hyperlend-related operations"""
@@ -41,7 +67,7 @@ class HyperlendClientProtocol(Protocol):
41
67
  buffer_bps: int | None = None,
42
68
  min_buffer_tokens: float | None = None,
43
69
  is_stable_symbol: bool | None = None,
44
- ) -> dict[str, Any]:
70
+ ) -> list[StableMarket]:
45
71
  """Fetch stable markets from Hyperlend"""
46
72
  ...
47
73
 
@@ -50,7 +76,7 @@ class HyperlendClientProtocol(Protocol):
50
76
  *,
51
77
  chain_id: int,
52
78
  user_address: str,
53
- ) -> dict[str, Any]:
79
+ ) -> AssetsView:
54
80
  """Fetch assets view for a user address from Hyperlend"""
55
81
  ...
56
82
 
@@ -59,7 +85,7 @@ class HyperlendClientProtocol(Protocol):
59
85
  *,
60
86
  chain_id: int,
61
87
  token_address: str,
62
- ) -> dict[str, Any]:
88
+ ) -> MarketEntry:
63
89
  """Fetch market entry from Hyperlend"""
64
90
  ...
65
91
 
@@ -69,35 +95,35 @@ class HyperlendClientProtocol(Protocol):
69
95
  chain_id: int,
70
96
  token_address: str,
71
97
  lookback_hours: int,
72
- ) -> dict[str, Any]:
98
+ ) -> LendRateHistory:
73
99
  """Fetch lend rate history from Hyperlend"""
74
100
  ...
75
101
 
76
102
 
77
103
  class LedgerClientProtocol(Protocol):
78
- """Protocol for vault transaction history and bookkeeping operations"""
104
+ """Protocol for strategy transaction history and bookkeeping operations"""
79
105
 
80
- async def get_vault_transactions(
106
+ async def get_strategy_transactions(
81
107
  self,
82
108
  *,
83
109
  wallet_address: str,
84
110
  limit: int = 50,
85
111
  offset: int = 0,
86
- ) -> dict[str, Any]:
87
- """Fetch a paginated list of transactions for a given vault wallet"""
112
+ ) -> StrategyTransactionList:
113
+ """Fetch a paginated list of transactions for a given strategy wallet"""
88
114
  ...
89
115
 
90
- async def get_vault_net_deposit(self, *, wallet_address: str) -> dict[str, Any]:
91
- """Fetch the net deposit (deposits - withdrawals) for a vault"""
116
+ async def get_strategy_net_deposit(self, *, wallet_address: str) -> NetDeposit:
117
+ """Fetch the net deposit (deposits - withdrawals) for a strategy"""
92
118
  ...
93
119
 
94
- async def get_vault_latest_transactions(
120
+ async def get_strategy_latest_transactions(
95
121
  self, *, wallet_address: str
96
- ) -> dict[str, Any]:
97
- """Fetch the latest transactions for a vault"""
122
+ ) -> StrategyTransactionList:
123
+ """Fetch the latest transactions for a strategy"""
98
124
  ...
99
125
 
100
- async def add_vault_deposit(
126
+ async def add_strategy_deposit(
101
127
  self,
102
128
  *,
103
129
  wallet_address: str,
@@ -107,11 +133,11 @@ class LedgerClientProtocol(Protocol):
107
133
  usd_value: str | float,
108
134
  data: dict[str, Any] | None = None,
109
135
  strategy_name: str | None = None,
110
- ) -> dict[str, Any]:
111
- """Record a deposit for a vault"""
136
+ ) -> TransactionRecord:
137
+ """Record a deposit for a strategy"""
112
138
  ...
113
139
 
114
- async def add_vault_withdraw(
140
+ async def add_strategy_withdraw(
115
141
  self,
116
142
  *,
117
143
  wallet_address: str,
@@ -121,32 +147,19 @@ class LedgerClientProtocol(Protocol):
121
147
  usd_value: str | float,
122
148
  data: dict[str, Any] | None = None,
123
149
  strategy_name: str | None = None,
124
- ) -> dict[str, Any]:
125
- """Record a withdrawal for a vault"""
150
+ ) -> TransactionRecord:
151
+ """Record a withdrawal for a strategy"""
126
152
  ...
127
153
 
128
- async def add_vault_operation(
154
+ async def add_strategy_operation(
129
155
  self,
130
156
  *,
131
157
  wallet_address: str,
132
158
  operation_data: dict[str, Any],
133
159
  usd_value: str | float,
134
160
  strategy_name: str | None = None,
135
- ) -> dict[str, Any]:
136
- """Record a vault operation (e.g., swaps, rebalances)"""
137
- ...
138
-
139
- async def add_vault_cashflow(
140
- self,
141
- *,
142
- wallet_address: str,
143
- block_timestamp: int,
144
- token_addr: str,
145
- amount: str | int | float,
146
- description: str,
147
- strategy_name: str | None = None,
148
- ) -> dict[str, Any]:
149
- """Record a cashflow for a vault (interest, funding, reward, or fee)"""
161
+ ) -> TransactionRecord:
162
+ """Record a strategy operation (e.g., swaps, rebalances)"""
150
163
  ...
151
164
 
152
165
 
@@ -159,7 +172,7 @@ class WalletClientProtocol(Protocol):
159
172
  token_id: str,
160
173
  wallet_address: str,
161
174
  human_readable: bool = True,
162
- ) -> dict[str, Any]:
175
+ ) -> TokenBalance:
163
176
  """Fetch a single token balance for an explicit wallet address"""
164
177
  ...
165
178
 
@@ -170,7 +183,7 @@ class WalletClientProtocol(Protocol):
170
183
  chain_id: int,
171
184
  user_address: str,
172
185
  human_readable: bool = True,
173
- ) -> dict[str, Any]:
186
+ ) -> PoolBalance:
174
187
  """Fetch a wallet's LP/share balance for a given pool address and chain"""
175
188
  ...
176
189
 
@@ -181,7 +194,7 @@ class WalletClientProtocol(Protocol):
181
194
  enrich: bool = True,
182
195
  from_cache: bool = False,
183
196
  add_llama: bool = True,
184
- ) -> dict[str, Any]:
197
+ ) -> EnrichedBalances:
185
198
  """Fetch all token balances for a wallet with enrichment"""
186
199
  ...
187
200
 
@@ -196,7 +209,7 @@ class TransactionClientProtocol(Protocol):
196
209
  token_address: str,
197
210
  amount: float,
198
211
  chain_id: int,
199
- ) -> dict[str, Any]:
212
+ ) -> TransactionPayload:
200
213
  """Build a send transaction payload for EVM tokens/native transfers"""
201
214
  ...
202
215
 
@@ -209,25 +222,19 @@ class PoolClientProtocol(Protocol):
209
222
  *,
210
223
  pool_ids: str,
211
224
  merge_external: bool | None = None,
212
- ) -> dict[str, Any]:
225
+ ) -> PoolList:
213
226
  """Fetch pools by comma-separated pool ids"""
214
227
  ...
215
228
 
216
- async def get_all_pools(
217
- self, *, merge_external: bool | None = None
218
- ) -> dict[str, Any]:
229
+ async def get_all_pools(self, *, merge_external: bool | None = None) -> PoolList:
219
230
  """Fetch all pools"""
220
231
  ...
221
232
 
222
- async def get_combined_pool_reports(self) -> dict[str, Any]:
223
- """Fetch combined pool reports"""
224
- ...
225
-
226
- async def get_llama_matches(self) -> dict[str, Any]:
233
+ async def get_llama_matches(self) -> dict[str, LlamaMatch]:
227
234
  """Fetch Llama matches for pools"""
228
235
  ...
229
236
 
230
- async def get_llama_reports(self, *, identifiers: str) -> dict[str, Any]:
237
+ async def get_llama_reports(self, *, identifiers: str) -> dict[str, LlamaReport]:
231
238
  """Fetch Llama reports using identifiers"""
232
239
  ...
233
240
 
@@ -247,7 +254,7 @@ class BRAPClientProtocol(Protocol):
247
254
  amount1: str,
248
255
  slippage: float | None = None,
249
256
  wayfinder_fee: float | None = None,
250
- ) -> dict[str, Any]:
257
+ ) -> BRAPQuote:
251
258
  """Get a quote for a bridge/swap operation"""
252
259
  ...
253
260
 
@@ -263,7 +270,7 @@ class SimulationClientProtocol(Protocol):
263
270
  amount: str,
264
271
  chain_id: int,
265
272
  initial_balances: dict[str, str],
266
- ) -> dict[str, Any]:
273
+ ) -> SimulationResult:
267
274
  """Simulate sending native ETH or ERC20 tokens"""
268
275
  ...
269
276
 
@@ -276,7 +283,7 @@ class SimulationClientProtocol(Protocol):
276
283
  chain_id: int,
277
284
  initial_balances: dict[str, str],
278
285
  clear_approval_first: bool = False,
279
- ) -> dict[str, Any]:
286
+ ) -> SimulationResult:
280
287
  """Simulate ERC20 token approval"""
281
288
  ...
282
289
 
@@ -290,6 +297,6 @@ class SimulationClientProtocol(Protocol):
290
297
  from_address: str,
291
298
  slippage: float,
292
299
  initial_balances: dict[str, str],
293
- ) -> dict[str, Any]:
300
+ ) -> SimulationResult:
294
301
  """Simulate token swap operation"""
295
302
  ...
@@ -31,11 +31,6 @@ class CachedTokenClient:
31
31
  async def get_gas_token(self, chain_code: str) -> dict[str, Any]:
32
32
  return await self._default_client.get_gas_token(chain_code)
33
33
 
34
- async def is_native_token(
35
- self, token_address: str, chain_id: int
36
- ) -> dict[str, Any]:
37
- return await self._default_client.is_native_token(token_address, chain_id)
38
-
39
34
 
40
35
  class MockHyperlendClient:
41
36
  """Mock client for testing"""