wayfinder-paths 0.1.21__py3-none-any.whl → 0.1.23__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 (63) hide show
  1. wayfinder_paths/__init__.py +0 -4
  2. wayfinder_paths/adapters/balance_adapter/README.md +0 -1
  3. wayfinder_paths/adapters/balance_adapter/adapter.py +65 -169
  4. wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -113
  5. wayfinder_paths/adapters/brap_adapter/README.md +22 -75
  6. wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
  7. wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
  8. wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
  9. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +39 -86
  10. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +5 -1
  11. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +6 -5
  12. wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
  13. wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
  14. wayfinder_paths/adapters/moonwell_adapter/adapter.py +108 -198
  15. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +37 -23
  16. wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
  17. wayfinder_paths/core/__init__.py +0 -3
  18. wayfinder_paths/core/clients/BRAPClient.py +3 -0
  19. wayfinder_paths/core/clients/ClientManager.py +0 -7
  20. wayfinder_paths/core/clients/LedgerClient.py +196 -172
  21. wayfinder_paths/core/clients/WayfinderClient.py +0 -1
  22. wayfinder_paths/core/clients/__init__.py +0 -5
  23. wayfinder_paths/core/clients/protocols.py +0 -13
  24. wayfinder_paths/core/config.py +0 -164
  25. wayfinder_paths/core/constants/__init__.py +58 -2
  26. wayfinder_paths/core/constants/base.py +8 -22
  27. wayfinder_paths/core/constants/chains.py +36 -0
  28. wayfinder_paths/core/constants/contracts.py +39 -0
  29. wayfinder_paths/core/constants/tokens.py +9 -0
  30. wayfinder_paths/core/strategies/Strategy.py +0 -10
  31. wayfinder_paths/core/utils/evm_helpers.py +5 -15
  32. wayfinder_paths/core/utils/tokens.py +28 -0
  33. wayfinder_paths/core/utils/transaction.py +13 -7
  34. wayfinder_paths/core/utils/web3.py +5 -3
  35. wayfinder_paths/policies/enso.py +1 -2
  36. wayfinder_paths/policies/hyper_evm.py +6 -3
  37. wayfinder_paths/policies/hyperlend.py +1 -2
  38. wayfinder_paths/policies/moonwell.py +12 -7
  39. wayfinder_paths/policies/prjx.py +1 -3
  40. wayfinder_paths/run_strategy.py +97 -300
  41. wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
  42. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +19 -14
  43. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +12 -11
  44. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +20 -33
  45. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +21 -18
  46. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -130
  47. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +32 -42
  48. {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/METADATA +3 -4
  49. {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/RECORD +51 -60
  50. {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/WHEEL +1 -1
  51. wayfinder_paths/core/clients/WalletClient.py +0 -41
  52. wayfinder_paths/core/engine/StrategyJob.py +0 -110
  53. wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
  54. wayfinder_paths/templates/adapter/README.md +0 -150
  55. wayfinder_paths/templates/adapter/adapter.py +0 -16
  56. wayfinder_paths/templates/adapter/examples.json +0 -8
  57. wayfinder_paths/templates/adapter/test_adapter.py +0 -30
  58. wayfinder_paths/templates/strategy/README.md +0 -186
  59. wayfinder_paths/templates/strategy/examples.json +0 -11
  60. wayfinder_paths/templates/strategy/strategy.py +0 -35
  61. wayfinder_paths/templates/strategy/test_strategy.py +0 -166
  62. wayfinder_paths/tests/test_smoke_manifest.py +0 -63
  63. {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/LICENSE +0 -0
@@ -1,13 +1,10 @@
1
1
  __version__ = "0.1.0"
2
2
 
3
- # Re-export commonly used items for convenience
4
3
  from wayfinder_paths.core import (
5
4
  BaseAdapter,
6
- LiquidationResult,
7
5
  StatusDict,
8
6
  StatusTuple,
9
7
  Strategy,
10
- StrategyJob,
11
8
  )
12
9
 
13
10
  __all__ = [
@@ -16,5 +13,4 @@ __all__ = [
16
13
  "Strategy",
17
14
  "StatusDict",
18
15
  "StatusTuple",
19
- "StrategyJob",
20
16
  ]
@@ -93,7 +93,6 @@ config = {
93
93
 
94
94
  ## Dependencies
95
95
 
96
- - `WalletClient` - For balance queries
97
96
  - `TokenClient` - For token metadata
98
97
  - `LedgerAdapter` - For transaction recording
99
98
  - `TokenAdapter` - For price lookups
@@ -4,9 +4,8 @@ from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
4
4
  from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
5
5
  from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
6
6
  from wayfinder_paths.core.clients.TokenClient import TokenClient
7
- from wayfinder_paths.core.clients.WalletClient import WalletClient
8
7
  from wayfinder_paths.core.utils.evm_helpers import resolve_chain_id
9
- from wayfinder_paths.core.utils.tokens import build_send_transaction
8
+ from wayfinder_paths.core.utils.tokens import build_send_transaction, get_token_balance
10
9
  from wayfinder_paths.core.utils.transaction import send_transaction
11
10
 
12
11
 
@@ -22,61 +21,27 @@ class BalanceAdapter(BaseAdapter):
22
21
  super().__init__("balance", config)
23
22
  self.main_wallet_signing_callback = main_wallet_signing_callback
24
23
  self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
25
- self.wallet_client = WalletClient()
26
24
  self.token_client = TokenClient()
27
25
  self.token_adapter = TokenAdapter()
28
26
  self.ledger_adapter = LedgerAdapter()
29
27
 
30
- def _parse_balance(self, raw: Any) -> int:
31
- if raw is None:
32
- return 0
33
- try:
34
- return int(raw)
35
- except (ValueError, TypeError):
36
- try:
37
- return int(float(raw))
38
- except (ValueError, TypeError):
39
- return 0
40
-
41
28
  async def get_balance(
42
29
  self,
43
30
  *,
44
- query: str | dict[str, Any] | None = None,
45
- token_id: str | None = None,
46
31
  wallet_address: str,
32
+ token_id: str | None = None,
33
+ token_address: str | None = None,
47
34
  chain_id: int | None = None,
48
- ) -> tuple[bool, str | int]:
49
- effective_query = query if query is not None else token_id
50
- resolved = (
51
- effective_query
52
- if isinstance(effective_query, str)
53
- else (effective_query or {}).get("token_id")
54
- )
55
- if not resolved:
56
- return (False, "missing query")
35
+ ) -> tuple[bool, int | str]:
57
36
  try:
58
- if chain_id is None:
59
- token_info = await self.token_client.get_token_details(resolved)
60
- if not token_info:
61
- return (False, f"Token not found: {resolved}")
62
- resolved_chain_id = resolve_chain_id(token_info, self.logger)
63
- if resolved_chain_id is None:
64
- return (False, f"Token {resolved} is missing a chain id")
65
- chain_id = resolved_chain_id
66
-
67
- data = await self.wallet_client.get_token_balance_for_address(
68
- wallet_address=wallet_address,
69
- query=resolved,
70
- chain_id=int(chain_id),
71
- )
72
- raw = (
73
- data.get("balance_raw") or data.get("balance")
74
- if isinstance(data, dict)
75
- else None
76
- )
77
- return (True, self._parse_balance(raw))
37
+ if token_id and not token_address:
38
+ token_info = await self.token_client.get_token_details(token_id)
39
+ token_address = token_info["address"]
40
+ chain_id = chain_id or resolve_chain_id(token_info)
41
+ balance = await get_token_balance(token_address, chain_id, wallet_address)
42
+ return True, balance
78
43
  except Exception as e:
79
- return (False, str(e))
44
+ return False, str(e)
80
45
 
81
46
  async def move_from_main_wallet_to_strategy_wallet(
82
47
  self,
@@ -84,16 +49,17 @@ class BalanceAdapter(BaseAdapter):
84
49
  amount: float,
85
50
  strategy_name: str = "unknown",
86
51
  skip_ledger: bool = False,
87
- ) -> tuple[bool, Any]:
52
+ ) -> tuple[bool, str]:
88
53
  return await self._move_between_wallets(
89
54
  token_id=token_id,
90
55
  amount=amount,
91
- from_wallet=self.config.get("main_wallet"),
92
- to_wallet=self.config.get("strategy_wallet"),
93
- ledger_method=self.ledger_adapter.record_deposit,
56
+ from_wallet=self.config["main_wallet"],
57
+ to_wallet=self.config["strategy_wallet"],
58
+ ledger_method=self.ledger_adapter.record_deposit
59
+ if not skip_ledger
60
+ else None,
94
61
  ledger_wallet="to",
95
62
  strategy_name=strategy_name,
96
- skip_ledger=skip_ledger,
97
63
  )
98
64
 
99
65
  async def move_from_strategy_wallet_to_main_wallet(
@@ -102,55 +68,36 @@ class BalanceAdapter(BaseAdapter):
102
68
  amount: float,
103
69
  strategy_name: str = "unknown",
104
70
  skip_ledger: bool = False,
105
- ) -> tuple[bool, Any]:
71
+ ) -> tuple[bool, str]:
106
72
  return await self._move_between_wallets(
107
73
  token_id=token_id,
108
74
  amount=amount,
109
- from_wallet=self.config.get("strategy_wallet"),
110
- to_wallet=self.config.get("main_wallet"),
111
- ledger_method=self.ledger_adapter.record_withdrawal,
75
+ from_wallet=self.config["strategy_wallet"],
76
+ to_wallet=self.config["main_wallet"],
77
+ ledger_method=self.ledger_adapter.record_withdrawal
78
+ if not skip_ledger
79
+ else None,
112
80
  ledger_wallet="from",
113
81
  strategy_name=strategy_name,
114
- skip_ledger=skip_ledger,
115
82
  )
116
83
 
117
84
  async def send_to_address(
118
85
  self,
119
86
  token_id: str,
120
87
  amount: int,
121
- from_wallet: dict[str, Any] | None,
88
+ from_wallet: dict[str, Any],
122
89
  to_address: str,
123
- signing_callback=None,
124
- skip_ledger: bool = True,
125
- ) -> tuple[bool, Any]:
126
- from_address = self._wallet_address(from_wallet)
127
- if not from_address:
128
- return False, "from_wallet missing or invalid"
129
-
130
- if not to_address:
131
- return False, "to_address is required"
132
-
90
+ signing_callback,
91
+ ) -> tuple[bool, str]:
133
92
  token_info = await self.token_client.get_token_details(token_id)
134
- if not token_info:
135
- return False, f"Token not found: {token_id}"
136
-
137
- chain_id = resolve_chain_id(token_info, self.logger)
138
- if chain_id is None:
139
- return False, f"Token {token_id} is missing chain_id"
140
-
141
- token_address = token_info.get("address")
142
-
93
+ chain_id = resolve_chain_id(token_info)
143
94
  tx = await build_send_transaction(
144
- from_address=from_address,
95
+ from_address=from_wallet["address"],
145
96
  to_address=to_address,
146
- token_address=token_address,
97
+ token_address=token_info["address"],
147
98
  chain_id=chain_id,
148
- amount=int(amount),
99
+ amount=amount,
149
100
  )
150
-
151
- if not signing_callback:
152
- return False, "signing_callback is required"
153
-
154
101
  tx_hash = await send_transaction(tx, signing_callback)
155
102
  return True, tx_hash
156
103
 
@@ -159,79 +106,63 @@ class BalanceAdapter(BaseAdapter):
159
106
  *,
160
107
  token_id: str,
161
108
  amount: float,
162
- from_wallet: dict[str, Any] | None,
163
- to_wallet: dict[str, Any] | None,
109
+ from_wallet: dict[str, Any],
110
+ to_wallet: dict[str, Any],
164
111
  ledger_method,
165
112
  ledger_wallet: str,
166
113
  strategy_name: str,
167
- skip_ledger: bool,
168
- ) -> tuple[bool, Any]:
169
- from_address = self._wallet_address(from_wallet)
170
- to_address = self._wallet_address(to_wallet)
171
- if not from_address or not to_address:
172
- return False, "main_wallet or strategy_wallet missing"
173
-
114
+ ) -> tuple[bool, str]:
174
115
  token_info = await self.token_client.get_token_details(token_id)
175
- if not token_info:
176
- return False, f"Token not found: {token_id}"
177
-
178
- chain_id = resolve_chain_id(token_info, self.logger)
179
- if chain_id is None:
180
- return False, f"Token {token_id} is missing chain_id"
181
-
116
+ chain_id = resolve_chain_id(token_info)
182
117
  decimals = token_info.get("decimals", 18)
183
118
  raw_amount = int(amount * (10**decimals))
184
119
 
185
- tx = await build_send_transaction(
186
- from_address=from_address,
187
- to_address=to_address,
188
- token_address=token_info.get("address"),
120
+ transaction = await build_send_transaction(
121
+ from_address=from_wallet["address"],
122
+ to_address=to_wallet["address"],
123
+ token_address=token_info["address"],
189
124
  chain_id=chain_id,
190
125
  amount=raw_amount,
191
126
  )
192
- broadcast_result = await self._send_tx(tx, from_address)
193
127
 
194
- if broadcast_result[0] and not skip_ledger and ledger_method is not None:
195
- wallet_for_ledger = from_address if ledger_wallet == "from" else to_address
128
+ main_address = self.config.get("main_wallet", {}).get("address", "").lower()
129
+ callback = (
130
+ self.main_wallet_signing_callback
131
+ if from_wallet["address"].lower() == main_address
132
+ else self.strategy_wallet_signing_callback
133
+ )
134
+ tx_hash = await send_transaction(transaction, callback)
135
+
136
+ if ledger_method:
137
+ wallet_for_ledger = (
138
+ from_wallet["address"]
139
+ if ledger_wallet == "from"
140
+ else to_wallet["address"]
141
+ )
196
142
  await self._record_ledger_entry(
197
- ledger_method=ledger_method,
198
- wallet_address=wallet_for_ledger,
199
- token_info=token_info,
200
- amount=amount,
201
- strategy_name=strategy_name,
143
+ ledger_method, wallet_for_ledger, token_info, amount, strategy_name
202
144
  )
203
145
 
204
- return broadcast_result
205
-
206
- async def _send_tx(self, tx: dict[str, Any], from_address: str) -> tuple[bool, Any]:
207
- main_wallet = self.config.get("main_wallet") or {}
208
- main_addr = main_wallet.get("address", "").lower()
209
-
210
- if from_address.lower() == main_addr:
211
- callback = self.main_wallet_signing_callback
212
- else:
213
- callback = self.strategy_wallet_signing_callback
214
-
215
- txn_hash = await send_transaction(tx, callback)
216
- return True, txn_hash
146
+ return True, tx_hash
217
147
 
218
148
  async def _record_ledger_entry(
219
149
  self,
220
- *,
221
150
  ledger_method,
222
151
  wallet_address: str,
223
152
  token_info: dict[str, Any],
224
153
  amount: float,
225
154
  strategy_name: str,
226
155
  ) -> None:
227
- chain_id = resolve_chain_id(token_info, self.logger)
228
- if chain_id is None:
229
- return
230
-
231
- usd_value = await self._token_amount_usd(token_info, amount)
232
156
  try:
233
- token_id = token_info.get("token_id") or token_info.get("id")
234
- success, response = await ledger_method(
157
+ chain_id = resolve_chain_id(token_info)
158
+ token_id = token_info.get("token_id")
159
+ usd_value = (
160
+ await self.token_adapter.get_amount_usd(
161
+ token_info.get("token_id"), amount, decimals=0
162
+ )
163
+ or 0.0
164
+ )
165
+ await ledger_method(
235
166
  wallet_address=wallet_address,
236
167
  chain_id=chain_id,
237
168
  token_address=token_info.get("address"),
@@ -244,40 +175,5 @@ class BalanceAdapter(BaseAdapter):
244
175
  },
245
176
  strategy_name=strategy_name,
246
177
  )
247
- if not success:
248
- self.logger.warning(
249
- "Ledger entry failed",
250
- wallet=wallet_address,
251
- token_id=token_id,
252
- amount=amount,
253
- error=response,
254
- )
255
- except Exception as exc: # noqa: BLE001
256
- token_id = token_info.get("token_id") or token_info.get("id")
257
- self.logger.warning(
258
- f"Ledger entry raised: {exc}",
259
- wallet=wallet_address,
260
- token_id=token_id,
261
- )
262
-
263
- async def _token_amount_usd(
264
- self, token_info: dict[str, Any], amount: float
265
- ) -> float:
266
- token_id = token_info.get("token_id")
267
- if not token_id:
268
- return 0.0
269
- success, price_data = await self.token_adapter.get_token_price(token_id)
270
- if not success or not price_data:
271
- return 0.0
272
- return float(price_data.get("current_price", 0.0)) * float(amount)
273
-
274
- def _wallet_address(self, wallet: dict[str, Any] | None) -> str | None:
275
- if not wallet:
276
- return None
277
- address = wallet.get("address")
278
- if address:
279
- return str(address)
280
- evm_wallet = wallet.get("evm") if isinstance(wallet, dict) else None
281
- if isinstance(evm_wallet, dict):
282
- return evm_wallet.get("address")
283
- return None
178
+ except Exception as exc:
179
+ self.logger.warning(f"Ledger entry failed: {exc}", wallet=wallet_address)
@@ -6,27 +6,15 @@ from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
6
6
 
7
7
 
8
8
  class TestBalanceAdapter:
9
- @pytest.fixture
10
- def mock_wallet_client(self):
11
- mock_client = AsyncMock()
12
- return mock_client
13
-
14
9
  @pytest.fixture
15
10
  def mock_token_client(self):
16
- mock_client = AsyncMock()
17
- return mock_client
11
+ return AsyncMock()
18
12
 
19
13
  @pytest.fixture
20
- def adapter(self, mock_wallet_client, mock_token_client):
21
- with (
22
- patch(
23
- "wayfinder_paths.adapters.balance_adapter.adapter.WalletClient",
24
- return_value=mock_wallet_client,
25
- ),
26
- patch(
27
- "wayfinder_paths.adapters.balance_adapter.adapter.TokenClient",
28
- return_value=mock_token_client,
29
- ),
14
+ def adapter(self, mock_token_client):
15
+ with patch(
16
+ "wayfinder_paths.adapters.balance_adapter.adapter.TokenClient",
17
+ return_value=mock_token_client,
30
18
  ):
31
19
  return BalanceAdapter(config={})
32
20
 
@@ -45,118 +33,58 @@ class TestBalanceAdapter:
45
33
  assert adapter.adapter_type == "BALANCE"
46
34
 
47
35
  @pytest.mark.asyncio
48
- async def test_get_balance_with_query_string(
49
- self, adapter, mock_token_client, mock_wallet_client
50
- ):
36
+ async def test_get_balance_with_token_id(self, adapter, mock_token_client):
51
37
  mock_token_client.get_token_details = AsyncMock(
52
38
  return_value={
53
39
  "token_id": "usd-coin-base",
54
- "address": "0x123",
40
+ "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
55
41
  "chain": {"id": 8453, "code": "base"},
56
42
  }
57
43
  )
58
- mock_wallet_client.get_token_balance_for_address = AsyncMock(
59
- return_value={"balance": 1000000}
60
- )
61
44
 
62
- success, balance = await adapter.get_balance(
63
- query="usd-coin-base",
64
- wallet_address="0xWallet",
65
- )
66
-
67
- assert success
68
- assert balance == 1000000
69
- mock_token_client.get_token_details.assert_called_once_with("usd-coin-base")
70
- mock_wallet_client.get_token_balance_for_address.assert_called_once_with(
71
- wallet_address="0xWallet",
72
- query="usd-coin-base",
73
- chain_id=8453,
74
- )
45
+ with patch(
46
+ "wayfinder_paths.adapters.balance_adapter.adapter.get_token_balance",
47
+ new_callable=AsyncMock,
48
+ return_value=1000000,
49
+ ) as mock_get_balance:
50
+ success, balance = await adapter.get_balance(
51
+ token_id="usd-coin-base",
52
+ wallet_address="0xWallet",
53
+ )
54
+
55
+ assert success
56
+ assert balance == 1000000
57
+ mock_token_client.get_token_details.assert_called_once_with("usd-coin-base")
58
+ mock_get_balance.assert_called_once_with(
59
+ "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", 8453, "0xWallet"
60
+ )
75
61
 
76
62
  @pytest.mark.asyncio
77
- async def test_get_balance_with_query_dict(
78
- self, adapter, mock_token_client, mock_wallet_client
79
- ):
80
- mock_token_client.get_token_details = AsyncMock(
81
- return_value={
82
- "token_id": "wsteth-base",
83
- "address": "0x456",
84
- "chain": {"id": 8453, "code": "base"},
85
- }
86
- )
87
- mock_wallet_client.get_token_balance_for_address = AsyncMock(
88
- return_value={"balance": 3000000}
89
- )
90
-
91
- success, balance = await adapter.get_balance(
92
- query={"token_id": "wsteth-base"},
93
- wallet_address="0x123",
94
- )
95
- assert success
96
- assert balance == 3000000
97
- mock_token_client.get_token_details.assert_called_once_with("wsteth-base")
98
- mock_wallet_client.get_token_balance_for_address.assert_called_once_with(
99
- wallet_address="0x123",
100
- query="wsteth-base",
101
- chain_id=8453,
102
- )
103
-
104
- @pytest.mark.asyncio
105
- async def test_get_balance_missing_query(self, adapter):
106
- success, result = await adapter.get_balance(query={}, wallet_address="0xabc")
107
- assert success is False
108
- assert "missing query" in str(result)
109
-
110
- @pytest.mark.asyncio
111
- async def test_get_balance_with_pool_address(
112
- self, adapter, mock_token_client, mock_wallet_client
113
- ):
114
- mock_wallet_client.get_token_balance_for_address = AsyncMock(
115
- return_value={"balance": 5000000}
116
- )
117
- mock_token_client.get_token_details = AsyncMock()
118
-
119
- success, balance = await adapter.get_balance(
120
- query="0xPoolAddress",
121
- wallet_address="0xWallet",
122
- chain_id=8453,
123
- )
124
-
125
- assert success
126
- assert balance == 5000000
127
- mock_wallet_client.get_token_balance_for_address.assert_called_once_with(
128
- wallet_address="0xWallet",
129
- query="0xPoolAddress",
130
- chain_id=8453,
131
- )
132
- mock_token_client.get_token_details.assert_not_called()
63
+ async def test_get_balance_with_token_address(self, adapter, mock_token_client):
64
+ with patch(
65
+ "wayfinder_paths.adapters.balance_adapter.adapter.get_token_balance",
66
+ new_callable=AsyncMock,
67
+ return_value=5000000,
68
+ ) as mock_get_balance:
69
+ success, balance = await adapter.get_balance(
70
+ token_address="0xTokenAddress",
71
+ wallet_address="0xWallet",
72
+ chain_id=8453,
73
+ )
74
+
75
+ assert success
76
+ assert balance == 5000000
77
+ mock_get_balance.assert_called_once_with("0xTokenAddress", 8453, "0xWallet")
78
+ mock_token_client.get_token_details.assert_not_called()
133
79
 
134
80
  @pytest.mark.asyncio
135
81
  async def test_get_balance_token_not_found(self, adapter, mock_token_client):
136
82
  mock_token_client.get_token_details = AsyncMock(return_value=None)
137
83
 
138
84
  success, error = await adapter.get_balance(
139
- query="invalid-token",
140
- wallet_address="0xWallet",
141
- )
142
-
143
- assert success is False
144
- assert "Token not found" in str(error)
145
-
146
- @pytest.mark.asyncio
147
- async def test_get_balance_missing_chain_id(self, adapter, mock_token_client):
148
- mock_token_client.get_token_details = AsyncMock(
149
- return_value={
150
- "token_id": "token-without-chain",
151
- "address": "0x123",
152
- "chain": {},
153
- }
154
- )
155
-
156
- success, error = await adapter.get_balance(
157
- query="token-without-chain",
85
+ token_id="invalid-token",
158
86
  wallet_address="0xWallet",
159
87
  )
160
88
 
161
89
  assert success is False
162
- assert "missing a chain id" in str(error)
90
+ assert "NoneType" in error or "subscriptable" in error