wayfinder-paths 0.1.23__py3-none-any.whl → 0.1.25__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.
- wayfinder_paths/__init__.py +2 -0
- wayfinder_paths/adapters/balance_adapter/adapter.py +250 -0
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -11
- wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
- wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
- wayfinder_paths/adapters/boros_adapter/client.py +476 -0
- wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
- wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
- wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
- wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
- wayfinder_paths/adapters/boros_adapter/types.py +70 -0
- wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
- wayfinder_paths/adapters/brap_adapter/adapter.py +1 -1
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +161 -26
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +77 -13
- wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +585 -61
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +592 -400
- wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +126 -219
- wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
- wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
- wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
- wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
- wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
- wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
- wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
- wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
- wayfinder_paths/adapters/token_adapter/examples.json +0 -4
- wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
- wayfinder_paths/conftest.py +24 -17
- wayfinder_paths/core/__init__.py +2 -0
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
- wayfinder_paths/core/adapters/models.py +17 -7
- wayfinder_paths/core/clients/BRAPClient.py +1 -1
- wayfinder_paths/core/clients/TokenClient.py +47 -1
- wayfinder_paths/core/clients/WayfinderClient.py +1 -2
- wayfinder_paths/core/clients/protocols.py +21 -22
- wayfinder_paths/core/clients/test_ledger_client.py +448 -0
- wayfinder_paths/core/config.py +12 -0
- wayfinder_paths/core/constants/__init__.py +15 -0
- wayfinder_paths/core/constants/base.py +6 -1
- wayfinder_paths/core/constants/contracts.py +39 -26
- wayfinder_paths/core/constants/erc20_abi.py +0 -1
- wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
- wayfinder_paths/core/constants/hyperliquid.py +16 -0
- wayfinder_paths/core/constants/moonwell_abi.py +0 -15
- wayfinder_paths/core/engine/manifest.py +66 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -61
- wayfinder_paths/core/strategies/__init__.py +10 -1
- wayfinder_paths/core/strategies/opa_loop.py +167 -0
- wayfinder_paths/core/utils/test_transaction.py +289 -0
- wayfinder_paths/core/utils/transaction.py +44 -1
- wayfinder_paths/core/utils/web3.py +3 -0
- wayfinder_paths/mcp/__init__.py +5 -0
- wayfinder_paths/mcp/preview.py +185 -0
- wayfinder_paths/mcp/scripting.py +84 -0
- wayfinder_paths/mcp/server.py +52 -0
- wayfinder_paths/mcp/state/profile_store.py +195 -0
- wayfinder_paths/mcp/state/store.py +89 -0
- wayfinder_paths/mcp/test_scripting.py +267 -0
- wayfinder_paths/mcp/tools/__init__.py +0 -0
- wayfinder_paths/mcp/tools/balances.py +290 -0
- wayfinder_paths/mcp/tools/discovery.py +158 -0
- wayfinder_paths/mcp/tools/execute.py +770 -0
- wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
- wayfinder_paths/mcp/tools/quotes.py +288 -0
- wayfinder_paths/mcp/tools/run_script.py +286 -0
- wayfinder_paths/mcp/tools/strategies.py +188 -0
- wayfinder_paths/mcp/tools/tokens.py +46 -0
- wayfinder_paths/mcp/tools/wallets.py +354 -0
- wayfinder_paths/mcp/utils.py +129 -0
- wayfinder_paths/policies/hyperliquid.py +1 -1
- wayfinder_paths/policies/lifi.py +18 -0
- wayfinder_paths/policies/util.py +8 -2
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +28 -119
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
- wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
- wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
- wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
- wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
- wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
- wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
- wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
- wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
- wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
- wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
- wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
- wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
- wayfinder_paths/strategies/boros_hype_strategy/test_strategy.py +202 -0
- wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
- wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +3 -12
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +7 -29
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +63 -40
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +0 -34
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +11 -34
- wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
- wayfinder_paths/tests/test_test_coverage.py +1 -4
- wayfinder_paths-0.1.25.dist-info/METADATA +377 -0
- wayfinder_paths-0.1.25.dist-info/RECORD +185 -0
- wayfinder_paths/scripts/create_strategy.py +0 -139
- wayfinder_paths/scripts/make_wallets.py +0 -142
- wayfinder_paths-0.1.23.dist-info/METADATA +0 -354
- wayfinder_paths-0.1.23.dist-info/RECORD +0 -120
- /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
- {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.25.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.25.dist-info}/WHEEL +0 -0
wayfinder_paths/conftest.py
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
# Add repo root to path so tests.test_utils can be imported
|
|
7
|
+
_repo_root = Path(__file__).parent.parent
|
|
8
|
+
_repo_root_str = str(_repo_root)
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def pytest_configure(config):
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
sys.path.remove(
|
|
15
|
-
sys.path.insert(0,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
config.addinivalue_line("markers", "smoke: mark test as a smoke test")
|
|
13
|
+
if _repo_root_str not in sys.path:
|
|
14
|
+
sys.path.insert(0, _repo_root_str)
|
|
15
|
+
elif sys.path.index(_repo_root_str) > 0:
|
|
16
|
+
sys.path.remove(_repo_root_str)
|
|
17
|
+
sys.path.insert(0, _repo_root_str)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def pytest_collection_modifyitems(config, items):
|
|
21
|
+
for item in items:
|
|
22
|
+
if "smoke" in item.nodeid:
|
|
23
|
+
item.add_marker(pytest.mark.smoke)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if _repo_root_str not in sys.path:
|
|
27
|
+
sys.path.insert(0, _repo_root_str)
|
|
28
|
+
elif sys.path.index(_repo_root_str) > 0:
|
|
29
|
+
sys.path.remove(_repo_root_str)
|
|
30
|
+
sys.path.insert(0, _repo_root_str)
|
wayfinder_paths/core/__init__.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
2
2
|
from wayfinder_paths.core.strategies.Strategy import (
|
|
3
|
+
LiquidationResult,
|
|
3
4
|
StatusDict,
|
|
4
5
|
StatusTuple,
|
|
5
6
|
Strategy,
|
|
@@ -7,6 +8,7 @@ from wayfinder_paths.core.strategies.Strategy import (
|
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
9
10
|
"Strategy",
|
|
11
|
+
"LiquidationResult",
|
|
10
12
|
"StatusDict",
|
|
11
13
|
"StatusTuple",
|
|
12
14
|
"BaseAdapter",
|
|
@@ -14,30 +14,5 @@ class BaseAdapter(ABC):
|
|
|
14
14
|
self.config = config or {}
|
|
15
15
|
self.logger = logger.bind(adapter=self.__class__.__name__)
|
|
16
16
|
|
|
17
|
-
async def connect(self) -> bool:
|
|
18
|
-
return True
|
|
19
|
-
|
|
20
|
-
async def get_balance(self, asset: str) -> dict[str, Any]:
|
|
21
|
-
if not asset or not isinstance(asset, str) or not asset.strip():
|
|
22
|
-
raise ValueError("asset must be a non-empty string")
|
|
23
|
-
raise NotImplementedError(
|
|
24
|
-
f"get_balance not supported by {self.__class__.__name__}"
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
async def health_check(self) -> dict[str, Any]:
|
|
28
|
-
try:
|
|
29
|
-
connected = await self.connect()
|
|
30
|
-
return {
|
|
31
|
-
"status": "healthy" if connected else "unhealthy",
|
|
32
|
-
"connected": connected,
|
|
33
|
-
"adapter": self.adapter_type or self.__class__.__name__,
|
|
34
|
-
}
|
|
35
|
-
except Exception as e:
|
|
36
|
-
return {
|
|
37
|
-
"status": "error",
|
|
38
|
-
"error": str(e),
|
|
39
|
-
"adapter": self.adapter_type or self.__class__.__name__,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
17
|
async def close(self) -> None:
|
|
43
18
|
pass
|
|
@@ -4,9 +4,11 @@ from pydantic import BaseModel, Field
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class OperationBase(BaseModel):
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
# These are provided by adapters at execution time, but tests and callers may
|
|
8
|
+
# construct operations without them (e.g., for bookkeeping payloads).
|
|
9
|
+
adapter: str = "unknown"
|
|
10
|
+
transaction_hash: str | None = None
|
|
11
|
+
transaction_chain_id: int | None = None
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class SWAP(OperationBase):
|
|
@@ -23,14 +25,22 @@ class SWAP(OperationBase):
|
|
|
23
25
|
|
|
24
26
|
class LEND(OperationBase):
|
|
25
27
|
type: Literal["LEND"] = "LEND"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
token_address: str
|
|
29
|
+
pool_address: str
|
|
30
|
+
amount: str
|
|
31
|
+
amount_usd: float
|
|
32
|
+
transaction_status: str | None = None
|
|
33
|
+
transaction_receipt: dict[str, Any] | None = None
|
|
28
34
|
|
|
29
35
|
|
|
30
36
|
class UNLEND(OperationBase):
|
|
31
37
|
type: Literal["UNLEND"] = "UNLEND"
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
token_address: str
|
|
39
|
+
pool_address: str
|
|
40
|
+
amount: str
|
|
41
|
+
amount_usd: float
|
|
42
|
+
transaction_status: str | None = None
|
|
43
|
+
transaction_receipt: dict[str, Any] | None = None
|
|
34
44
|
|
|
35
45
|
|
|
36
46
|
# Type alias for operation types (currently only SWAP is used)
|
|
@@ -72,7 +72,7 @@ class BRAPQuoteResponse(TypedDict):
|
|
|
72
72
|
class BRAPClient(WayfinderClient):
|
|
73
73
|
def __init__(self):
|
|
74
74
|
super().__init__()
|
|
75
|
-
self.api_base_url = f"{get_api_base_url()}/
|
|
75
|
+
self.api_base_url = f"{get_api_base_url()}/blockchain/braps"
|
|
76
76
|
|
|
77
77
|
async def get_quote(
|
|
78
78
|
self,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import xml.etree.ElementTree as ET
|
|
3
4
|
from typing import NotRequired, Required, TypedDict
|
|
4
5
|
|
|
5
6
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
@@ -76,10 +77,20 @@ class GasToken(TypedDict):
|
|
|
76
77
|
chain: NotRequired[ChainInfo]
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
class FuzzyTokenResult(TypedDict):
|
|
81
|
+
coingecko_id: NotRequired[str]
|
|
82
|
+
address: NotRequired[str]
|
|
83
|
+
chain: NotRequired[str]
|
|
84
|
+
name: NotRequired[str]
|
|
85
|
+
symbol: NotRequired[str]
|
|
86
|
+
price: NotRequired[float]
|
|
87
|
+
confidence: NotRequired[int]
|
|
88
|
+
|
|
89
|
+
|
|
79
90
|
class TokenClient(WayfinderClient):
|
|
80
91
|
def __init__(self):
|
|
81
92
|
super().__init__()
|
|
82
|
-
self.api_base_url = f"{get_api_base_url()}/
|
|
93
|
+
self.api_base_url = f"{get_api_base_url()}/blockchain/tokens"
|
|
83
94
|
|
|
84
95
|
async def get_token_details(
|
|
85
96
|
self, query: str, market_data: bool = False, chain_id: int | None = None
|
|
@@ -103,3 +114,38 @@ class TokenClient(WayfinderClient):
|
|
|
103
114
|
response.raise_for_status()
|
|
104
115
|
data = response.json()
|
|
105
116
|
return data.get("data", data)
|
|
117
|
+
|
|
118
|
+
async def fuzzy_search(
|
|
119
|
+
self, query: str, chain: str | None = None
|
|
120
|
+
) -> dict[str, list[FuzzyTokenResult] | str | None]:
|
|
121
|
+
url = f"{self.api_base_url}/fuzzy/"
|
|
122
|
+
params: dict[str, str] = {"query": query}
|
|
123
|
+
if chain:
|
|
124
|
+
params["chain"] = chain
|
|
125
|
+
response = await self._authed_request("GET", url, params=params)
|
|
126
|
+
response.raise_for_status()
|
|
127
|
+
trace_id = response.headers.get("X-Token-Fuzzy-Trace")
|
|
128
|
+
tokens = self._parse_fuzzy_xml(response.text)
|
|
129
|
+
return {"tokens": tokens, "trace_id": trace_id}
|
|
130
|
+
|
|
131
|
+
def _parse_fuzzy_xml(self, xml_content: str) -> list[FuzzyTokenResult]:
|
|
132
|
+
root = ET.fromstring(xml_content)
|
|
133
|
+
tokens: list[FuzzyTokenResult] = []
|
|
134
|
+
for token_elem in root.findall("token"):
|
|
135
|
+
token: FuzzyTokenResult = {}
|
|
136
|
+
for field in ["coingecko_id", "address", "chain", "name", "symbol"]:
|
|
137
|
+
elem = token_elem.find(field)
|
|
138
|
+
if elem is not None and elem.text:
|
|
139
|
+
token[field] = elem.text # type: ignore[literal-required]
|
|
140
|
+
for num_field in ["price", "confidence"]:
|
|
141
|
+
elem = token_elem.find(num_field)
|
|
142
|
+
if elem is not None and elem.text:
|
|
143
|
+
try:
|
|
144
|
+
if num_field == "price":
|
|
145
|
+
token["price"] = float(elem.text)
|
|
146
|
+
else:
|
|
147
|
+
token["confidence"] = int(elem.text)
|
|
148
|
+
except ValueError:
|
|
149
|
+
pass
|
|
150
|
+
tokens.append(token)
|
|
151
|
+
return tokens
|
|
@@ -65,8 +65,7 @@ class WayfinderClient:
|
|
|
65
65
|
logger.debug(f"Making {method} request to {url}")
|
|
66
66
|
start_time = time.time()
|
|
67
67
|
|
|
68
|
-
#
|
|
69
|
-
# This ensures API keys are passed to all endpoints (including public ones) for rate limiting
|
|
68
|
+
# Pass API key to all endpoints (including public ones) for rate limiting
|
|
70
69
|
if not self.headers.get("X-API-KEY"):
|
|
71
70
|
creds = self._load_config_credentials()
|
|
72
71
|
api_key = creds.get("api_key")
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)
|
|
3
|
+
from typing import Any, Protocol
|
|
4
|
+
|
|
5
|
+
from wayfinder_paths.core.clients.BRAPClient import BRAPQuoteResponse
|
|
6
|
+
from wayfinder_paths.core.clients.HyperlendClient import (
|
|
7
|
+
AssetsView,
|
|
8
|
+
LendRateHistory,
|
|
9
|
+
MarketEntry,
|
|
10
|
+
StableMarketsHeadroomResponse,
|
|
11
|
+
)
|
|
12
|
+
from wayfinder_paths.core.clients.LedgerClient import (
|
|
13
|
+
StrategyTransactionList,
|
|
14
|
+
TransactionRecord,
|
|
15
|
+
)
|
|
16
|
+
from wayfinder_paths.core.clients.PoolClient import (
|
|
17
|
+
LlamaMatchesResponse,
|
|
18
|
+
PoolList,
|
|
19
|
+
)
|
|
20
|
+
from wayfinder_paths.core.clients.TokenClient import (
|
|
21
|
+
GasToken,
|
|
22
|
+
TokenDetails,
|
|
23
|
+
)
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class TokenClientProtocol(Protocol):
|