wayfinder-paths 0.1.29__py3-none-any.whl → 0.1.31__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 (43) hide show
  1. wayfinder_paths/adapters/boros_adapter/adapter.py +313 -12
  2. wayfinder_paths/adapters/boros_adapter/test_adapter.py +125 -14
  3. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +17 -3
  4. wayfinder_paths/adapters/hyperliquid_adapter/exchange.py +5 -5
  5. wayfinder_paths/adapters/hyperliquid_adapter/local_signer.py +2 -33
  6. wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +1 -1
  7. wayfinder_paths/adapters/hyperliquid_adapter/test_cancel_order.py +57 -0
  8. wayfinder_paths/adapters/hyperliquid_adapter/test_exchange_mid_prices.py +52 -0
  9. wayfinder_paths/adapters/hyperliquid_adapter/test_hyperliquid_sdk_live.py +64 -0
  10. wayfinder_paths/adapters/hyperliquid_adapter/util.py +9 -10
  11. wayfinder_paths/core/clients/PoolClient.py +1 -1
  12. wayfinder_paths/core/constants/hype_oft_abi.py +151 -0
  13. wayfinder_paths/core/strategies/Strategy.py +1 -2
  14. wayfinder_paths/mcp/tools/execute.py +48 -16
  15. wayfinder_paths/mcp/tools/hyperliquid.py +1 -13
  16. wayfinder_paths/mcp/tools/quotes.py +38 -124
  17. wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +24 -0
  18. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +249 -29
  19. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +125 -15
  20. wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +57 -201
  21. wayfinder_paths/strategies/boros_hype_strategy/constants.py +1 -152
  22. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +29 -0
  23. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +2 -0
  24. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/manifest.yaml +33 -0
  25. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2 -0
  26. wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +23 -0
  27. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +2 -0
  28. wayfinder_paths/tests/test_manifests.py +93 -0
  29. wayfinder_paths/tests/test_mcp_balances.py +73 -0
  30. wayfinder_paths/tests/test_mcp_discovery.py +34 -0
  31. wayfinder_paths/tests/test_mcp_execute.py +146 -0
  32. wayfinder_paths/tests/test_mcp_hyperliquid_execute.py +69 -0
  33. wayfinder_paths/tests/test_mcp_idempotency_store.py +14 -0
  34. wayfinder_paths/tests/test_mcp_quote_swap.py +60 -0
  35. wayfinder_paths/tests/test_mcp_run_script.py +47 -0
  36. wayfinder_paths/tests/test_mcp_tokens.py +49 -0
  37. wayfinder_paths/tests/test_mcp_utils.py +35 -0
  38. wayfinder_paths/tests/test_mcp_wallets.py +38 -0
  39. {wayfinder_paths-0.1.29.dist-info → wayfinder_paths-0.1.31.dist-info}/METADATA +2 -2
  40. {wayfinder_paths-0.1.29.dist-info → wayfinder_paths-0.1.31.dist-info}/RECORD +42 -25
  41. {wayfinder_paths-0.1.29.dist-info → wayfinder_paths-0.1.31.dist-info}/WHEEL +1 -1
  42. wayfinder_paths/core/types.py +0 -19
  43. {wayfinder_paths-0.1.29.dist-info → wayfinder_paths-0.1.31.dist-info}/LICENSE +0 -0
@@ -243,7 +243,7 @@ class LegConfirmer:
243
243
  if asset_id is None:
244
244
  continue
245
245
  try:
246
- await self.adapter.cancel_order(asset_id, str(oid_int), address)
246
+ await self.adapter.cancel_order(asset_id, oid_int, address)
247
247
  attempted.add(oid_int)
248
248
  except Exception as exc:
249
249
  logger.info(f"Cancel failed for oid {oid_int}: {exc}")
@@ -0,0 +1,57 @@
1
+ from types import SimpleNamespace
2
+ from unittest.mock import AsyncMock
3
+
4
+ import pytest
5
+
6
+ from wayfinder_paths.adapters.hyperliquid_adapter.adapter import HyperliquidAdapter
7
+ from wayfinder_paths.adapters.hyperliquid_adapter.exchange import Exchange
8
+
9
+
10
+ class TestHyperliquidCancelOrder:
11
+ @pytest.mark.asyncio
12
+ async def test_exchange_cancel_order_uses_int_oid(self):
13
+ ex = Exchange(
14
+ info=SimpleNamespace(),
15
+ util=SimpleNamespace(),
16
+ sign_callback=AsyncMock(return_value="0x"),
17
+ signing_type="eip712",
18
+ )
19
+ ex.sign_and_broadcast_hypecore = AsyncMock(return_value={"status": "ok"})
20
+
21
+ await ex.cancel_order(asset_id=10210, order_id=306356655993, address="0xabc")
22
+
23
+ args, _ = ex.sign_and_broadcast_hypecore.await_args
24
+ action = args[0]
25
+ assert action["type"] == "cancel"
26
+ assert action["cancels"][0]["a"] == 10210
27
+ assert isinstance(action["cancels"][0]["o"], int)
28
+ assert action["cancels"][0]["o"] == 306356655993
29
+
30
+ @pytest.mark.asyncio
31
+ async def test_adapter_cancel_order_parses_string_oid(self):
32
+ adapter = object.__new__(HyperliquidAdapter)
33
+ adapter.simulation = False
34
+ adapter._exchange = SimpleNamespace()
35
+ adapter._exchange.cancel_order = AsyncMock(return_value={"status": "ok"})
36
+
37
+ ok, _ = await adapter.cancel_order(
38
+ asset_id=10210, order_id="306356655993", address="0xabc"
39
+ )
40
+ assert ok is True
41
+
42
+ adapter._exchange.cancel_order.assert_awaited_once_with(
43
+ asset_id=10210, order_id=306356655993, address="0xabc"
44
+ )
45
+
46
+ @pytest.mark.asyncio
47
+ async def test_adapter_cancel_order_rejects_bad_oid(self):
48
+ adapter = object.__new__(HyperliquidAdapter)
49
+ adapter.simulation = False
50
+ adapter._exchange = SimpleNamespace()
51
+ adapter._exchange.cancel_order = AsyncMock(return_value={"status": "ok"})
52
+
53
+ ok, res = await adapter.cancel_order(
54
+ asset_id=1, order_id="not-a-number", address="0xabc"
55
+ )
56
+ assert ok is False
57
+ assert res["status"] == "err"
@@ -0,0 +1,52 @@
1
+ from types import SimpleNamespace
2
+ from unittest.mock import AsyncMock
3
+
4
+ import pytest
5
+
6
+ from wayfinder_paths.adapters.hyperliquid_adapter.exchange import Exchange
7
+
8
+
9
+ class _InfoStub(SimpleNamespace):
10
+ def all_mids(self):
11
+ return {"HYPE": "1.0"}
12
+
13
+ async def all_dex_mid_prices(self): # pragma: no cover
14
+ raise AssertionError(
15
+ "Exchange must use Info.all_mids(), not all_dex_mid_prices"
16
+ )
17
+
18
+
19
+ class _UtilStub(SimpleNamespace):
20
+ def get_price_decimals_for_hypecore_asset(self, asset_id: int) -> int:
21
+ return 6
22
+
23
+
24
+ class TestExchangeMidPriceFetch:
25
+ @pytest.mark.asyncio
26
+ async def test_place_market_order_uses_all_mids(self):
27
+ info = _InfoStub(asset_to_coin={7: "HYPE"})
28
+ util = _UtilStub()
29
+ ex = Exchange(
30
+ info=info,
31
+ util=util,
32
+ sign_callback=AsyncMock(return_value="0x"),
33
+ signing_type="eip712",
34
+ )
35
+
36
+ async def _no_broadcast(action, address):
37
+ return action
38
+
39
+ ex.sign_and_broadcast_hypecore = _no_broadcast
40
+
41
+ action = await ex.place_market_order(
42
+ asset_id=7,
43
+ is_buy=True,
44
+ slippage=0.01,
45
+ size=1.0,
46
+ address="0xabc",
47
+ )
48
+
49
+ assert action["type"] == "order"
50
+ assert action["orders"][0]["a"] == 7
51
+ assert action["orders"][0]["b"] is True
52
+ assert action["orders"][0]["p"] == "1.01"
@@ -0,0 +1,64 @@
1
+ from unittest.mock import AsyncMock
2
+
3
+ import pytest
4
+
5
+ from wayfinder_paths.adapters.hyperliquid_adapter.adapter import HyperliquidAdapter
6
+ from wayfinder_paths.adapters.hyperliquid_adapter.exchange import Exchange
7
+
8
+
9
+ @pytest.fixture
10
+ def live_adapter():
11
+ return HyperliquidAdapter(config={}, simulation=True)
12
+
13
+
14
+ class TestHyperliquidSdkCompat:
15
+ @pytest.mark.asyncio
16
+ async def test_util_mid_prices_live(self, live_adapter):
17
+ mids = await live_adapter.util.get_hypecore_all_dex_mid_prices()
18
+ assert isinstance(mids, dict)
19
+ assert len(mids) > 0
20
+
21
+ # Sanity check some common keys exist (perp coins or spot tickers like @107).
22
+ assert "HYPE" in mids or "BTC" in mids or any(k.startswith("@") for k in mids)
23
+
24
+ @pytest.mark.asyncio
25
+ async def test_util_meta_live(self, live_adapter):
26
+ meta = await live_adapter.util.get_hypecore_all_dex_meta_universe()
27
+ assert isinstance(meta, dict)
28
+ assert "universe" in meta
29
+
30
+
31
+ class TestExchangeUsesLiveMids:
32
+ @pytest.mark.asyncio
33
+ async def test_place_market_order_builds_ioc_limit(self, live_adapter):
34
+ # Use a perp id to avoid spot naming edge-cases.
35
+ asset_id = live_adapter.coin_to_asset["HYPE"]
36
+
37
+ ex = Exchange(
38
+ info=live_adapter.info,
39
+ util=live_adapter.util,
40
+ sign_callback=AsyncMock(return_value="0x"),
41
+ signing_type="eip712",
42
+ )
43
+
44
+ async def _no_broadcast(action, address):
45
+ return action
46
+
47
+ ex.sign_and_broadcast_hypecore = _no_broadcast
48
+
49
+ action = await ex.place_market_order(
50
+ asset_id=asset_id,
51
+ is_buy=True,
52
+ slippage=0.01,
53
+ size=1.0,
54
+ address="0x0000000000000000000000000000000000000000",
55
+ )
56
+
57
+ assert action["type"] == "order"
58
+ assert action["orders"][0]["a"] == asset_id
59
+ assert action["orders"][0]["b"] is True
60
+
61
+ # Price should be at/above current mid for a buy (within rounding tolerance).
62
+ mid = float(live_adapter.info.all_mids()["HYPE"])
63
+ px = float(action["orders"][0]["p"])
64
+ assert px >= mid * 0.999
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import time
3
2
  from decimal import ROUND_DOWN, Decimal
4
3
  from typing import Any
@@ -61,10 +60,12 @@ class Util:
61
60
  return assets.get(asset_name)
62
61
 
63
62
  async def get_hypecore_all_dex_mid_prices(self):
64
- return await self.info.all_dex_mid_prices()
63
+ # Backwards compatible wrapper: the Hyperliquid SDK now exposes `all_mids()`.
64
+ return self.info.all_mids()
65
65
 
66
66
  async def get_hypecore_all_dex_meta_universe(self):
67
- return await self.info.all_dex_meta_universe()
67
+ # Backwards compatible wrapper: the Hyperliquid SDK now exposes `meta()`.
68
+ return self.info.meta()
68
69
 
69
70
  def get_size_decimals_for_hypecore_asset(self, asset_id: int):
70
71
  return self.info.asset_to_sz_decimals[asset_id]
@@ -107,11 +108,9 @@ class Util:
107
108
  return perp_user_state
108
109
 
109
110
  async def get_hypecore_user(self, address):
110
- perp_user_state, spot_user_state, open_orders = await asyncio.gather(
111
- self.info.all_dex_user_state(address),
112
- self.info.spot_user_state(address),
113
- self.info.all_dex_open_orders(address),
114
- )
111
+ perp_user_state = self.info.user_state(address)
112
+ spot_user_state = self.info.spot_user_state(address)
113
+ open_orders = self.info.open_orders(address)
115
114
  formatted_perp_state = self._reformat_perp_user_state(perp_user_state)
116
115
  state = {
117
116
  "perp_user_state": formatted_perp_state,
@@ -172,7 +171,7 @@ class Util:
172
171
  out = []
173
172
  while True:
174
173
  try:
175
- batch = await self.info.user_fills_by_time(wallet, start, end, False)
174
+ batch = self.info.user_fills_by_time(wallet, start, end, False)
176
175
  except Exception as e:
177
176
  logger.error(f"Failed to fetch fills via node/public/SDK: {e}")
178
177
  break
@@ -185,7 +184,7 @@ class Util:
185
184
  return out
186
185
 
187
186
  async def get_hypecore_position(self, address, asset_name):
188
- perp_user_state = await self.info.all_dex_user_state(address)
187
+ perp_user_state = self.info.user_state(address)
189
188
  formatted_perp_user_state = self._reformat_perp_user_state(perp_user_state)
190
189
 
191
190
  for pos in formatted_perp_user_state.get("assetPositions", []):
@@ -99,7 +99,7 @@ class PoolClient(WayfinderClient):
99
99
  self.api_base_url = get_api_base_url()
100
100
 
101
101
  def _pools_url(self) -> str:
102
- return f"{self.api_base_url}/v1/blockchain/pools/"
102
+ return f"{self.api_base_url}/blockchain/pools/"
103
103
 
104
104
  async def get_pools(
105
105
  self,
@@ -0,0 +1,151 @@
1
+ """LayerZero OFT ABI subset used for bridging native HYPE from HyperEVM."""
2
+
3
+ HYPE_OFT_ABI = [
4
+ {
5
+ "inputs": [
6
+ {
7
+ "components": [
8
+ {"internalType": "uint32", "name": "dstEid", "type": "uint32"},
9
+ {"internalType": "bytes32", "name": "to", "type": "bytes32"},
10
+ {"internalType": "uint256", "name": "amountLD", "type": "uint256"},
11
+ {
12
+ "internalType": "uint256",
13
+ "name": "minAmountLD",
14
+ "type": "uint256",
15
+ },
16
+ {
17
+ "internalType": "bytes",
18
+ "name": "extraOptions",
19
+ "type": "bytes",
20
+ },
21
+ {"internalType": "bytes", "name": "composeMsg", "type": "bytes"},
22
+ {"internalType": "bytes", "name": "oftCmd", "type": "bytes"},
23
+ ],
24
+ "internalType": "struct SendParam",
25
+ "name": "_sendParam",
26
+ "type": "tuple",
27
+ },
28
+ {"internalType": "bool", "name": "_payInLzToken", "type": "bool"},
29
+ ],
30
+ "name": "quoteSend",
31
+ "outputs": [
32
+ {
33
+ "components": [
34
+ {"internalType": "uint256", "name": "nativeFee", "type": "uint256"},
35
+ {
36
+ "internalType": "uint256",
37
+ "name": "lzTokenFee",
38
+ "type": "uint256",
39
+ },
40
+ ],
41
+ "internalType": "struct MessagingFee",
42
+ "name": "",
43
+ "type": "tuple",
44
+ }
45
+ ],
46
+ "stateMutability": "view",
47
+ "type": "function",
48
+ },
49
+ {
50
+ "inputs": [
51
+ {
52
+ "components": [
53
+ {"internalType": "uint32", "name": "dstEid", "type": "uint32"},
54
+ {"internalType": "bytes32", "name": "to", "type": "bytes32"},
55
+ {"internalType": "uint256", "name": "amountLD", "type": "uint256"},
56
+ {
57
+ "internalType": "uint256",
58
+ "name": "minAmountLD",
59
+ "type": "uint256",
60
+ },
61
+ {
62
+ "internalType": "bytes",
63
+ "name": "extraOptions",
64
+ "type": "bytes",
65
+ },
66
+ {"internalType": "bytes", "name": "composeMsg", "type": "bytes"},
67
+ {"internalType": "bytes", "name": "oftCmd", "type": "bytes"},
68
+ ],
69
+ "internalType": "struct SendParam",
70
+ "name": "_sendParam",
71
+ "type": "tuple",
72
+ },
73
+ {
74
+ "components": [
75
+ {"internalType": "uint256", "name": "nativeFee", "type": "uint256"},
76
+ {
77
+ "internalType": "uint256",
78
+ "name": "lzTokenFee",
79
+ "type": "uint256",
80
+ },
81
+ ],
82
+ "internalType": "struct MessagingFee",
83
+ "name": "_fee",
84
+ "type": "tuple",
85
+ },
86
+ {"internalType": "address", "name": "_refundAddress", "type": "address"},
87
+ ],
88
+ "name": "send",
89
+ "outputs": [
90
+ {
91
+ "components": [
92
+ {"internalType": "bytes32", "name": "guid", "type": "bytes32"},
93
+ {"internalType": "uint64", "name": "nonce", "type": "uint64"},
94
+ {
95
+ "components": [
96
+ {
97
+ "internalType": "uint256",
98
+ "name": "nativeFee",
99
+ "type": "uint256",
100
+ },
101
+ {
102
+ "internalType": "uint256",
103
+ "name": "lzTokenFee",
104
+ "type": "uint256",
105
+ },
106
+ ],
107
+ "internalType": "struct MessagingFee",
108
+ "name": "fee",
109
+ "type": "tuple",
110
+ },
111
+ ],
112
+ "internalType": "struct MessagingReceipt",
113
+ "name": "",
114
+ "type": "tuple",
115
+ },
116
+ {
117
+ "components": [
118
+ {
119
+ "internalType": "uint256",
120
+ "name": "amountSentLD",
121
+ "type": "uint256",
122
+ },
123
+ {
124
+ "internalType": "uint256",
125
+ "name": "amountReceivedLD",
126
+ "type": "uint256",
127
+ },
128
+ ],
129
+ "internalType": "struct OFTReceipt",
130
+ "name": "",
131
+ "type": "tuple",
132
+ },
133
+ ],
134
+ "stateMutability": "payable",
135
+ "type": "function",
136
+ },
137
+ {
138
+ "inputs": [],
139
+ "name": "sharedDecimals",
140
+ "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}],
141
+ "stateMutability": "view",
142
+ "type": "function",
143
+ },
144
+ {
145
+ "inputs": [],
146
+ "name": "decimalConversionRate",
147
+ "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
148
+ "stateMutability": "view",
149
+ "type": "function",
150
+ },
151
+ ]
@@ -8,7 +8,6 @@ from loguru import logger
8
8
 
9
9
  from wayfinder_paths.core.clients.TokenClient import TokenDetails
10
10
  from wayfinder_paths.core.strategies.descriptors import StratDescriptor
11
- from wayfinder_paths.core.types import HyperliquidSignCallback
12
11
 
13
12
 
14
13
  class StatusDict(TypedDict):
@@ -52,7 +51,7 @@ class Strategy(ABC):
52
51
  main_wallet_signing_callback: Callable[[dict], Awaitable[str]] | None = None,
53
52
  strategy_wallet_signing_callback: Callable[[dict], Awaitable[str]]
54
53
  | None = None,
55
- strategy_sign_typed_data: HyperliquidSignCallback | None = None,
54
+ strategy_sign_typed_data: Callable[[dict], Awaitable[str]] | None = None,
56
55
  ):
57
56
  self.ledger_adapter = None
58
57
  self.logger = logger.bind(strategy=self.__class__.__name__)
@@ -257,19 +257,40 @@ def _compact_quote(
257
257
  """Create a compact summary of quote data, stripping verbose nested structures."""
258
258
  result: dict[str, Any] = {}
259
259
 
260
- # Extract provider list from quotes (API returns quotes as a list at top level)
261
- all_quotes = quote_data.get("quotes", [])
262
- if not isinstance(all_quotes, list):
263
- all_quotes = []
264
- if isinstance(all_quotes, list):
265
- result["providers"] = list(
266
- {
267
- q.get("provider")
268
- for q in all_quotes
269
- if isinstance(q, dict) and q.get("provider")
270
- }
271
- )
272
- result["quote_count"] = len(all_quotes)
260
+ # Extract provider list from quotes. BRAP quotes may appear as either:
261
+ # 1) {"quotes": [...], "best_quote": {...}}
262
+ # 2) {"quotes": {"all_quotes": [...], "best_quote": {...}, "quote_count": N}}
263
+ all_quotes: list[dict[str, Any]] = []
264
+ raw_quotes = quote_data.get("quotes", [])
265
+ quote_count = None
266
+
267
+ if isinstance(raw_quotes, list):
268
+ all_quotes = [q for q in raw_quotes if isinstance(q, dict)]
269
+ elif isinstance(raw_quotes, dict):
270
+ nested = raw_quotes.get("all_quotes") or raw_quotes.get("quotes") or []
271
+ if isinstance(nested, list):
272
+ all_quotes = [q for q in nested if isinstance(q, dict)]
273
+ qc = raw_quotes.get("quote_count")
274
+ try:
275
+ quote_count = int(qc) if qc is not None else None
276
+ except (TypeError, ValueError):
277
+ quote_count = None
278
+
279
+ providers: list[str] = []
280
+ seen: set[str] = set()
281
+ for q in all_quotes:
282
+ p = q.get("provider")
283
+ if not p:
284
+ continue
285
+ p_str = str(p)
286
+ if p_str in seen:
287
+ continue
288
+ seen.add(p_str)
289
+ providers.append(p_str)
290
+
291
+ if providers:
292
+ result["providers"] = providers
293
+ result["quote_count"] = quote_count if quote_count is not None else len(all_quotes)
273
294
 
274
295
  # Compact best_quote - only essential fields
275
296
  if isinstance(best_quote, dict):
@@ -511,9 +532,20 @@ async def execute(
511
532
  store.put(key, tool_input, response)
512
533
  return response
513
534
 
514
- best_quote = (
515
- quote_data.get("best_quote") if isinstance(quote_data, dict) else None
516
- )
535
+ # BRAP quote responses have historically appeared in two shapes:
536
+ # 1) {"quotes": [...], "best_quote": {...}}
537
+ # 2) {"quotes": {"all_quotes": [...], "best_quote": {...}, "quote_count": N}}
538
+ best_quote = None
539
+ if isinstance(quote_data, dict):
540
+ if isinstance(quote_data.get("best_quote"), dict):
541
+ best_quote = quote_data.get("best_quote")
542
+ else:
543
+ quotes_block = quote_data.get("quotes")
544
+ if isinstance(quotes_block, dict) and isinstance(
545
+ quotes_block.get("best_quote"), dict
546
+ ):
547
+ best_quote = quotes_block.get("best_quote")
548
+
517
549
  if not isinstance(best_quote, dict):
518
550
  response = err(
519
551
  "quote_error", "No best_quote returned", {"quote": quote_data}
@@ -5,9 +5,6 @@ import re
5
5
  from typing import Any, Literal
6
6
 
7
7
  from wayfinder_paths.adapters.hyperliquid_adapter.adapter import HyperliquidAdapter
8
- from wayfinder_paths.adapters.hyperliquid_adapter.executor import (
9
- LocalHyperliquidExecutor,
10
- )
11
8
  from wayfinder_paths.core.constants.hyperliquid import (
12
9
  DEFAULT_HYPERLIQUID_BUILDER_FEE_TENTHS_BP,
13
10
  HYPE_FEE_WALLET,
@@ -404,16 +401,7 @@ async def hyperliquid_execute(
404
401
 
405
402
  effects: list[dict[str, Any]] = []
406
403
 
407
- executor = None
408
- if not dry:
409
- try:
410
- executor = LocalHyperliquidExecutor(config=config, network="mainnet")
411
- except Exception as exc: # noqa: BLE001
412
- response = err("executor_error", str(exc))
413
- store.put(key, tool_input, response)
414
- return response
415
-
416
- adapter = HyperliquidAdapter(config=config, simulation=dry, executor=executor)
404
+ adapter = HyperliquidAdapter(config=config, simulation=dry)
417
405
 
418
406
  if action == "withdraw":
419
407
  if amount_usdc is None: