wayfinder-paths 0.1.23__py3-none-any.whl → 0.1.24__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 (122) hide show
  1. wayfinder_paths/adapters/balance_adapter/adapter.py +250 -0
  2. wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -11
  4. wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
  5. wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
  6. wayfinder_paths/adapters/boros_adapter/client.py +476 -0
  7. wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
  8. wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
  9. wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
  10. wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
  11. wayfinder_paths/adapters/boros_adapter/types.py +70 -0
  12. wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
  13. wayfinder_paths/adapters/brap_adapter/adapter.py +1 -1
  14. wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
  15. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +161 -26
  16. wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
  17. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +77 -13
  18. wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
  19. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +585 -61
  20. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
  21. wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
  22. wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
  23. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
  24. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
  25. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
  26. wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
  27. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
  28. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
  29. wayfinder_paths/adapters/moonwell_adapter/adapter.py +592 -400
  30. wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
  31. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +126 -219
  32. wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
  33. wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
  34. wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
  35. wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
  36. wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
  37. wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
  38. wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
  39. wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
  40. wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
  41. wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
  42. wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
  43. wayfinder_paths/adapters/token_adapter/examples.json +0 -4
  44. wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
  45. wayfinder_paths/conftest.py +24 -17
  46. wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
  47. wayfinder_paths/core/adapters/models.py +17 -7
  48. wayfinder_paths/core/clients/BRAPClient.py +1 -1
  49. wayfinder_paths/core/clients/TokenClient.py +47 -1
  50. wayfinder_paths/core/clients/WayfinderClient.py +1 -2
  51. wayfinder_paths/core/clients/protocols.py +21 -22
  52. wayfinder_paths/core/clients/test_ledger_client.py +448 -0
  53. wayfinder_paths/core/config.py +12 -0
  54. wayfinder_paths/core/constants/__init__.py +15 -0
  55. wayfinder_paths/core/constants/base.py +6 -1
  56. wayfinder_paths/core/constants/contracts.py +39 -26
  57. wayfinder_paths/core/constants/erc20_abi.py +0 -1
  58. wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
  59. wayfinder_paths/core/constants/hyperliquid.py +16 -0
  60. wayfinder_paths/core/constants/moonwell_abi.py +0 -15
  61. wayfinder_paths/core/engine/manifest.py +66 -0
  62. wayfinder_paths/core/strategies/Strategy.py +0 -61
  63. wayfinder_paths/core/strategies/__init__.py +10 -1
  64. wayfinder_paths/core/strategies/opa_loop.py +167 -0
  65. wayfinder_paths/core/utils/test_transaction.py +289 -0
  66. wayfinder_paths/core/utils/transaction.py +44 -1
  67. wayfinder_paths/core/utils/web3.py +3 -0
  68. wayfinder_paths/mcp/__init__.py +5 -0
  69. wayfinder_paths/mcp/preview.py +185 -0
  70. wayfinder_paths/mcp/scripting.py +84 -0
  71. wayfinder_paths/mcp/server.py +52 -0
  72. wayfinder_paths/mcp/state/profile_store.py +195 -0
  73. wayfinder_paths/mcp/state/store.py +89 -0
  74. wayfinder_paths/mcp/test_scripting.py +267 -0
  75. wayfinder_paths/mcp/tools/__init__.py +0 -0
  76. wayfinder_paths/mcp/tools/balances.py +290 -0
  77. wayfinder_paths/mcp/tools/discovery.py +158 -0
  78. wayfinder_paths/mcp/tools/execute.py +770 -0
  79. wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
  80. wayfinder_paths/mcp/tools/quotes.py +288 -0
  81. wayfinder_paths/mcp/tools/run_script.py +286 -0
  82. wayfinder_paths/mcp/tools/strategies.py +188 -0
  83. wayfinder_paths/mcp/tools/tokens.py +46 -0
  84. wayfinder_paths/mcp/tools/wallets.py +354 -0
  85. wayfinder_paths/mcp/utils.py +129 -0
  86. wayfinder_paths/policies/hyperliquid.py +1 -1
  87. wayfinder_paths/policies/lifi.py +18 -0
  88. wayfinder_paths/policies/util.py +8 -2
  89. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +28 -119
  90. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
  91. wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
  92. wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
  93. wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
  94. wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
  95. wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
  96. wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
  97. wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
  98. wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
  99. wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
  100. wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
  101. wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
  102. wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
  103. wayfinder_paths/strategies/boros_hype_strategy/test_strategy.py +202 -0
  104. wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
  105. wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
  106. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +3 -12
  107. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +7 -29
  108. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +63 -40
  109. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
  110. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +0 -34
  111. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +11 -34
  112. wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
  113. wayfinder_paths/tests/test_test_coverage.py +1 -4
  114. wayfinder_paths-0.1.24.dist-info/METADATA +378 -0
  115. wayfinder_paths-0.1.24.dist-info/RECORD +185 -0
  116. {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
  117. wayfinder_paths/scripts/create_strategy.py +0 -139
  118. wayfinder_paths/scripts/make_wallets.py +0 -142
  119. wayfinder_paths-0.1.23.dist-info/METADATA +0 -354
  120. wayfinder_paths-0.1.23.dist-info/RECORD +0 -120
  121. /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
  122. {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
@@ -1,33 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import uuid
4
- from collections.abc import Mapping
5
5
  from typing import Any
6
6
 
7
+ from eth_account import Account
8
+ from hyperliquid.exchange import Exchange
9
+ from hyperliquid.info import Info
10
+ from hyperliquid.utils import constants
11
+ from hyperliquid.utils.types import BuilderInfo, Cloid
7
12
  from loguru import logger
8
13
 
9
- from wayfinder_paths.core.clients.protocols import HyperliquidExecutorProtocol
10
-
11
- # Re-export for backwards compatibility with existing imports.
12
- HyperliquidExecutor = HyperliquidExecutorProtocol
13
-
14
- try:
15
- from eth_account import Account
16
- from hyperliquid.exchange import Exchange
17
- from hyperliquid.info import Info
18
- from hyperliquid.utils import constants
19
- from hyperliquid.utils.types import BuilderInfo, Cloid
20
-
21
- HYPERLIQUID_AVAILABLE = True
22
- except ImportError:
23
- HYPERLIQUID_AVAILABLE = False
24
- Account = None
25
- Exchange = None
26
- Info = None
27
- constants = None
28
- Cloid = None
29
- BuilderInfo = None
30
-
31
14
 
32
15
  def _new_client_id() -> Cloid:
33
16
  cloid_str = "0x" + uuid.uuid4().hex
@@ -41,11 +24,6 @@ class LocalHyperliquidExecutor:
41
24
  config: dict[str, Any],
42
25
  network: str = "mainnet",
43
26
  ) -> None:
44
- if not HYPERLIQUID_AVAILABLE:
45
- raise ImportError(
46
- "hyperliquid package not installed. Install with: poetry add hyperliquid"
47
- )
48
-
49
27
  self.config = config
50
28
  self.network = network
51
29
 
@@ -75,36 +53,15 @@ class LocalHyperliquidExecutor:
75
53
  f"LocalHyperliquidExecutor initialized for address: {self._wallet.address}"
76
54
  )
77
55
 
56
+ @property
57
+ def address(self) -> str:
58
+ """Return the wallet address."""
59
+ return self._wallet.address
60
+
78
61
  def _get_perp_coin(self, asset_id: int) -> str | None:
79
62
  if self._asset_id_to_coin is None:
80
- mapping: dict[int, str] = {}
81
-
82
- asset_to_coin = getattr(self.info, "asset_to_coin", None)
83
- if isinstance(asset_to_coin, Mapping):
84
- for k, v in asset_to_coin.items():
85
- try:
86
- asset_int = int(k)
87
- except (TypeError, ValueError):
88
- continue
89
- if v:
90
- mapping[asset_int] = str(v)
91
-
92
- coin_to_asset = getattr(self.info, "coin_to_asset", None)
93
- try:
94
- coin_to_asset_dict = dict(coin_to_asset) if coin_to_asset else {}
95
- except Exception: # noqa: BLE001
96
- coin_to_asset_dict = {}
97
- for coin, aid in coin_to_asset_dict.items():
98
- try:
99
- asset_int = int(aid)
100
- except (TypeError, ValueError):
101
- continue
102
- if coin and asset_int not in mapping:
103
- mapping[asset_int] = str(coin)
104
-
105
- self._asset_id_to_coin = mapping
106
-
107
- return self._asset_id_to_coin.get(asset_id) if self._asset_id_to_coin else None
63
+ self._asset_id_to_coin = {v: k for k, v in self.info.coin_to_asset.items()}
64
+ return self._asset_id_to_coin.get(asset_id)
108
65
 
109
66
  def _resolve_private_key(self, config: dict[str, Any]) -> str | None:
110
67
  # Try strategy_wallet first
@@ -125,10 +82,6 @@ class LocalHyperliquidExecutor:
125
82
 
126
83
  return None
127
84
 
128
- @property
129
- def address(self) -> str:
130
- return self._wallet.address
131
-
132
85
  async def place_market_order(
133
86
  self,
134
87
  *,
@@ -309,10 +262,24 @@ class LocalHyperliquidExecutor:
309
262
  }
310
263
 
311
264
  try:
312
- result = self.exchange.usd_class_transfer(
313
- amount=amount,
314
- to_perp=True,
315
- )
265
+ # Some SDK variants expose `hypecore_transfer` for usdClassTransfer.
266
+ # Prefer it when available; otherwise fall back to `usd_class_transfer`.
267
+ transfer_fn = getattr(self.exchange, "hypecore_transfer", None)
268
+ if callable(transfer_fn):
269
+ try:
270
+ result = transfer_fn(amount=amount, to_perp=True, address=address)
271
+ if asyncio.iscoroutine(result):
272
+ result = await result
273
+ except TypeError:
274
+ result = self.exchange.usd_class_transfer(
275
+ amount=amount,
276
+ to_perp=True,
277
+ )
278
+ else:
279
+ result = self.exchange.usd_class_transfer(
280
+ amount=amount,
281
+ to_perp=True,
282
+ )
316
283
  logger.debug(f"Spot to perp transfer result: {result}")
317
284
  return result
318
285
 
@@ -336,10 +303,22 @@ class LocalHyperliquidExecutor:
336
303
  }
337
304
 
338
305
  try:
339
- result = self.exchange.usd_class_transfer(
340
- amount=amount,
341
- to_perp=False,
342
- )
306
+ transfer_fn = getattr(self.exchange, "hypecore_transfer", None)
307
+ if callable(transfer_fn):
308
+ try:
309
+ result = transfer_fn(amount=amount, to_perp=False, address=address)
310
+ if asyncio.iscoroutine(result):
311
+ result = await result
312
+ except TypeError:
313
+ result = self.exchange.usd_class_transfer(
314
+ amount=amount,
315
+ to_perp=False,
316
+ )
317
+ else:
318
+ result = self.exchange.usd_class_transfer(
319
+ amount=amount,
320
+ to_perp=False,
321
+ )
343
322
  logger.debug(f"Perp to spot transfer result: {result}")
344
323
  return result
345
324
 
@@ -0,0 +1,14 @@
1
+ schema_version: "0.1"
2
+ entrypoint: "adapters.hyperliquid_adapter.adapter.HyperliquidAdapter"
3
+ capabilities:
4
+ - "market.read"
5
+ - "market.meta"
6
+ - "market.funding"
7
+ - "market.candles"
8
+ - "market.orderbook"
9
+ - "order.execute"
10
+ - "order.cancel"
11
+ - "position.manage"
12
+ - "transfer"
13
+ - "withdraw"
14
+ dependencies: []
@@ -6,12 +6,11 @@ import os
6
6
  import time
7
7
  from dataclasses import dataclass
8
8
  from decimal import ROUND_DOWN, ROUND_UP, Decimal
9
- from typing import TYPE_CHECKING, Any, Literal
9
+ from typing import Any, Literal
10
10
 
11
11
  from loguru import logger
12
12
 
13
- if TYPE_CHECKING:
14
- from wayfinder_paths.adapters.hyperliquid_adapter.adapter import HyperliquidAdapter
13
+ from wayfinder_paths.adapters.hyperliquid_adapter.adapter import HyperliquidAdapter
15
14
 
16
15
  MIN_NOTIONAL_USD = 10.0
17
16
 
@@ -3,6 +3,8 @@ from unittest.mock import MagicMock, patch
3
3
 
4
4
  import pytest
5
5
 
6
+ from wayfinder_paths.adapters.hyperliquid_adapter.adapter import HyperliquidAdapter
7
+
6
8
 
7
9
  class TestHyperliquidAdapter:
8
10
  @pytest.fixture
@@ -43,22 +45,9 @@ class TestHyperliquidAdapter:
43
45
  "wayfinder_paths.adapters.hyperliquid_adapter.adapter.constants",
44
46
  mock_constants,
45
47
  ):
46
- with patch(
47
- "wayfinder_paths.adapters.hyperliquid_adapter.adapter.HYPERLIQUID_AVAILABLE",
48
- True,
49
- ):
50
- from wayfinder_paths.adapters.hyperliquid_adapter.adapter import (
51
- HyperliquidAdapter,
52
- )
53
-
54
- adapter = HyperliquidAdapter(config={})
55
- adapter.info = mock_info
56
- return adapter
57
-
58
- @pytest.mark.asyncio
59
- async def test_connect(self, adapter):
60
- result = await adapter.connect()
61
- assert result is True
48
+ adapter = HyperliquidAdapter(config={})
49
+ adapter.info = mock_info
50
+ return adapter
62
51
 
63
52
  @pytest.mark.asyncio
64
53
  async def test_get_meta_and_asset_ctxs(self, adapter):
@@ -95,11 +84,6 @@ class TestHyperliquidAdapter:
95
84
  assert success
96
85
  assert "assetPositions" in data
97
86
 
98
- @pytest.mark.asyncio
99
- async def test_health_check(self, adapter):
100
- result = await adapter.health_check()
101
- assert result["status"] == "healthy"
102
-
103
87
  def test_get_sz_decimals(self, adapter):
104
88
  decimals = adapter.get_sz_decimals(0)
105
89
  assert decimals == 4
@@ -107,3 +91,15 @@ class TestHyperliquidAdapter:
107
91
  def test_get_sz_decimals_unknown_asset(self, adapter):
108
92
  with pytest.raises(ValueError, match="Unknown asset_id"):
109
93
  adapter.get_sz_decimals(99999)
94
+
95
+ @pytest.mark.asyncio
96
+ async def test_get_full_user_state(self, adapter):
97
+ adapter.info.frontend_open_orders.return_value = [{"oid": 1}]
98
+
99
+ ok, state = await adapter.get_full_user_state(account="0x1234")
100
+ assert ok is True
101
+ assert state["protocol"] == "hyperliquid"
102
+ assert state["account"] == "0x1234"
103
+ assert state["perp"] is not None
104
+ assert state["spot"] is not None
105
+ assert state["openOrders"] == [{"oid": 1}]
@@ -66,19 +66,16 @@ class TestSpotAssetIDs:
66
66
 
67
67
  @pytest.mark.asyncio
68
68
  async def test_get_spot_asset_id_helper(self, live_adapter):
69
- # First populate cache
70
69
  success, _ = await live_adapter.get_spot_assets()
71
70
  assert success
72
71
 
73
- # Now use sync helper
74
- purr_id = live_adapter.get_spot_asset_id("PURR", "USDC")
72
+ purr_id = await live_adapter.get_spot_asset_id("PURR", "USDC")
75
73
  assert purr_id == 10000
76
74
 
77
- hype_id = live_adapter.get_spot_asset_id("HYPE", "USDC")
75
+ hype_id = await live_adapter.get_spot_asset_id("HYPE", "USDC")
78
76
  assert hype_id == 10107
79
77
 
80
- # Non-existent should return None
81
- fake_id = live_adapter.get_spot_asset_id("FAKECOIN", "USDC")
78
+ fake_id = await live_adapter.get_spot_asset_id("FAKECOIN", "USDC")
82
79
  assert fake_id is None
83
80
 
84
81
 
@@ -3,6 +3,10 @@ from unittest.mock import MagicMock, patch
3
3
 
4
4
  import pytest
5
5
 
6
+ from wayfinder_paths.adapters.hyperliquid_adapter.executor import (
7
+ LocalHyperliquidExecutor,
8
+ )
9
+
6
10
 
7
11
  class TestLocalHyperliquidExecutor:
8
12
  @pytest.fixture
@@ -34,10 +38,6 @@ class TestLocalHyperliquidExecutor:
34
38
  "wayfinder_paths.adapters.hyperliquid_adapter.executor.Exchange",
35
39
  return_value=mock_exchange,
36
40
  ):
37
- from wayfinder_paths.adapters.hyperliquid_adapter.executor import (
38
- LocalHyperliquidExecutor,
39
- )
40
-
41
41
  executor = LocalHyperliquidExecutor(
42
42
  config={"strategy_wallet": {"private_key": "0x" + "11" * 32}}
43
43
  )
@@ -73,10 +73,6 @@ class TestLocalHyperliquidExecutor:
73
73
  "wayfinder_paths.adapters.hyperliquid_adapter.executor.Exchange",
74
74
  return_value=mock_exchange,
75
75
  ):
76
- from wayfinder_paths.adapters.hyperliquid_adapter.executor import (
77
- LocalHyperliquidExecutor,
78
- )
79
-
80
76
  executor = LocalHyperliquidExecutor(
81
77
  config={"strategy_wallet": {"private_key": "0x" + "11" * 32}}
82
78
  )
@@ -1,3 +1,5 @@
1
+ from decimal import Decimal
2
+
1
3
  import pytest
2
4
 
3
5
  from wayfinder_paths.adapters.hyperliquid_adapter.utils import (
@@ -147,8 +149,6 @@ class TestSzDecimalsForAsset:
147
149
 
148
150
  class TestSizeStep:
149
151
  def test_size_step_calculation(self):
150
- from decimal import Decimal
151
-
152
152
  asset_to_sz = {0: 4, 1: 2}
153
153
 
154
154
  assert size_step(asset_to_sz, 0) == Decimal("0.0001")
@@ -0,0 +1,7 @@
1
+ schema_version: "0.1"
2
+ entrypoint: "adapters.ledger_adapter.adapter.LedgerAdapter"
3
+ capabilities:
4
+ - "ledger.read"
5
+ - "ledger.record"
6
+ - "ledger.snapshot"
7
+ dependencies: []
@@ -3,6 +3,7 @@ from unittest.mock import AsyncMock
3
3
  import pytest
4
4
 
5
5
  from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
6
+ from wayfinder_paths.core.adapters.models import SWAP
6
7
 
7
8
 
8
9
  class TestLedgerAdapter:
@@ -138,8 +139,6 @@ class TestLedgerAdapter:
138
139
 
139
140
  @pytest.mark.asyncio
140
141
  async def test_record_operation_success(self, adapter, mock_ledger_client):
141
- from wayfinder_paths.core.adapters.models import SWAP
142
-
143
142
  mock_response = {
144
143
  "operation_id": "op_123",
145
144
  "status": "recorded",