wayfinder-paths 0.1.27__py3-none-any.whl → 0.1.29__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/adapters/boros_adapter/adapter.py +142 -12
- wayfinder_paths/adapters/boros_adapter/client.py +7 -5
- wayfinder_paths/adapters/boros_adapter/test_adapter.py +147 -18
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +2 -12
- wayfinder_paths/adapters/hyperliquid_adapter/exchange.py +8 -40
- wayfinder_paths/adapters/hyperliquid_adapter/local_signer.py +2 -2
- wayfinder_paths/adapters/multicall_adapter/adapter.py +2 -4
- wayfinder_paths/core/clients/TokenClient.py +1 -1
- wayfinder_paths/core/constants/__init__.py +23 -1
- wayfinder_paths/core/constants/contracts.py +22 -0
- wayfinder_paths/core/constants/hyperliquid.py +20 -3
- wayfinder_paths/core/engine/manifest.py +1 -1
- wayfinder_paths/mcp/scripting.py +2 -2
- wayfinder_paths/mcp/tools/discovery.py +3 -72
- wayfinder_paths/mcp/tools/execute.py +8 -4
- wayfinder_paths/mcp/tools/hyperliquid.py +1 -1
- wayfinder_paths/mcp/tools/quotes.py +7 -8
- wayfinder_paths/mcp/tools/wallets.py +4 -7
- wayfinder_paths/mcp/utils.py +0 -22
- wayfinder_paths/policies/lifi.py +5 -2
- wayfinder_paths/policies/moonwell.py +3 -1
- wayfinder_paths/policies/util.py +4 -2
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +1 -2
- wayfinder_paths/strategies/boros_hype_strategy/constants.py +23 -16
- wayfinder_paths/strategies/boros_hype_strategy/strategy.py +24 -63
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +2 -1
- {wayfinder_paths-0.1.27.dist-info → wayfinder_paths-0.1.29.dist-info}/METADATA +3 -2
- {wayfinder_paths-0.1.27.dist-info → wayfinder_paths-0.1.29.dist-info}/RECORD +30 -30
- {wayfinder_paths-0.1.27.dist-info → wayfinder_paths-0.1.29.dist-info}/WHEEL +1 -1
- {wayfinder_paths-0.1.27.dist-info → wayfinder_paths-0.1.29.dist-info}/LICENSE +0 -0
|
@@ -2,10 +2,27 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
6
|
+
ARBITRUM_USDC as ARBITRUM_USDC_ADDRESS,
|
|
7
|
+
)
|
|
8
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
9
|
+
HYPE_FEE_WALLET,
|
|
10
|
+
)
|
|
11
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
12
|
+
HYPERLIQUID_BRIDGE as HYPERLIQUID_BRIDGE_ADDRESS,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Re-export addresses for backwards compatibility
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ARBITRUM_USDC_ADDRESS",
|
|
18
|
+
"ARBITRUM_USDC_TOKEN_ID",
|
|
19
|
+
"HYPE_FEE_WALLET",
|
|
20
|
+
"HYPERLIQUID_BRIDGE_ADDRESS",
|
|
21
|
+
"DEFAULT_HYPERLIQUID_BUILDER_FEE_TENTHS_BP",
|
|
22
|
+
"DEFAULT_HYPERLIQUID_BUILDER_FEE",
|
|
23
|
+
]
|
|
24
|
+
|
|
7
25
|
ARBITRUM_USDC_TOKEN_ID: str = "usd-coin-arbitrum"
|
|
8
|
-
HYPE_FEE_WALLET: str = "0xaA1D89f333857eD78F8434CC4f896A9293EFE65c"
|
|
9
26
|
|
|
10
27
|
# Tenths of a basis point: 30 -> 0.030% (3 bps)
|
|
11
28
|
DEFAULT_HYPERLIQUID_BUILDER_FEE_TENTHS_BP: int = 30
|
|
@@ -19,7 +19,7 @@ class StrategyManifest(BaseModel):
|
|
|
19
19
|
)
|
|
20
20
|
name: str | None = Field(
|
|
21
21
|
default=None,
|
|
22
|
-
description="Unique name identifier for this strategy instance. Used to look up dedicated wallet in
|
|
22
|
+
description="Unique name identifier for this strategy instance. Used to look up dedicated wallet in config.json by label.",
|
|
23
23
|
)
|
|
24
24
|
permissions: dict[str, Any] = Field(default_factory=dict)
|
|
25
25
|
adapters: list[AdapterRequirement] = Field(default_factory=list)
|
wayfinder_paths/mcp/scripting.py
CHANGED
|
@@ -57,8 +57,8 @@ def get_adapter[T](
|
|
|
57
57
|
wallet = find_wallet_by_label(wallet_label)
|
|
58
58
|
if not wallet:
|
|
59
59
|
raise ValueError(
|
|
60
|
-
f"Wallet '{wallet_label}' not found in
|
|
61
|
-
"Run 'just create-wallets'
|
|
60
|
+
f"Wallet '{wallet_label}' not found in config.json. "
|
|
61
|
+
"Run 'just create-wallets'."
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
private_key = wallet.get("private_key") or wallet.get("private_key_hex")
|
|
@@ -6,12 +6,12 @@ from wayfinder_paths.mcp.utils import err, ok, read_text_excerpt, read_yaml, rep
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
async def discover(
|
|
9
|
-
kind: Literal["
|
|
9
|
+
kind: Literal["adapter", "strategy"],
|
|
10
10
|
detail: Literal["summary", "full"] = "summary",
|
|
11
11
|
) -> dict[str, Any]:
|
|
12
12
|
root = repo_root()
|
|
13
13
|
base = (
|
|
14
|
-
root / "wayfinder_paths" / ("adapters" if kind == "
|
|
14
|
+
root / "wayfinder_paths" / ("adapters" if kind == "adapter" else "strategies")
|
|
15
15
|
)
|
|
16
16
|
if not base.exists():
|
|
17
17
|
return err("not_found", f"Directory not found: {base}")
|
|
@@ -26,7 +26,7 @@ async def discover(
|
|
|
26
26
|
manifest = read_yaml(manifest_path)
|
|
27
27
|
rel_manifest = str(manifest_path.relative_to(root))
|
|
28
28
|
|
|
29
|
-
if kind == "
|
|
29
|
+
if kind == "adapter":
|
|
30
30
|
item = {
|
|
31
31
|
"name": child.name,
|
|
32
32
|
"entrypoint": manifest.get("entrypoint"),
|
|
@@ -58,75 +58,11 @@ async def discover(
|
|
|
58
58
|
return ok({"kind": kind, "items": items})
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def _default_claude_notes_for_adapter(name: str) -> dict[str, Any] | None:
|
|
62
|
-
# Keep this deliberately small; the real docs live in .claude/skills.
|
|
63
|
-
if name == "brap_adapter":
|
|
64
|
-
return {
|
|
65
|
-
"reads": [
|
|
66
|
-
{
|
|
67
|
-
"name": "quote_swap (via mcp__wayfinder__quote_swap)",
|
|
68
|
-
"summary": "Get best BRAP route + fees; no on-chain effects.",
|
|
69
|
-
}
|
|
70
|
-
],
|
|
71
|
-
"writes": [
|
|
72
|
-
{
|
|
73
|
-
"name": "swap (via mcp__wayfinder__execute kind=swap)",
|
|
74
|
-
"risk": "funds",
|
|
75
|
-
"summary": "May submit ERC20 approvals and a swap/bridge tx.",
|
|
76
|
-
}
|
|
77
|
-
],
|
|
78
|
-
"gotchas": [
|
|
79
|
-
"USDT-style tokens may require setting allowance to 0 before approve.",
|
|
80
|
-
"Cross-chain swaps still broadcast a tx on the source chain.",
|
|
81
|
-
],
|
|
82
|
-
}
|
|
83
|
-
if name == "balance_adapter":
|
|
84
|
-
return {
|
|
85
|
-
"reads": [
|
|
86
|
-
{
|
|
87
|
-
"name": "balances (via mcp__wayfinder__balances)",
|
|
88
|
-
"summary": "Read token/pool balances via Wayfinder API.",
|
|
89
|
-
}
|
|
90
|
-
],
|
|
91
|
-
"writes": [
|
|
92
|
-
{
|
|
93
|
-
"name": "send (via mcp__wayfinder__execute kind=send)",
|
|
94
|
-
"risk": "funds",
|
|
95
|
-
"summary": "Native/ERC20 sends from a local wallet.",
|
|
96
|
-
}
|
|
97
|
-
],
|
|
98
|
-
"gotchas": ["Prefer DRY_RUN=1 until ready to broadcast."],
|
|
99
|
-
}
|
|
100
|
-
if name == "hyperliquid_adapter":
|
|
101
|
-
return {
|
|
102
|
-
"reads": [
|
|
103
|
-
{
|
|
104
|
-
"name": "hyperliquid (via mcp__wayfinder__hyperliquid)",
|
|
105
|
-
"summary": "User state + market reads (no signing).",
|
|
106
|
-
}
|
|
107
|
-
],
|
|
108
|
-
"writes": [
|
|
109
|
-
{
|
|
110
|
-
"name": "hyperliquid_execute (via mcp__wayfinder__hyperliquid_execute)",
|
|
111
|
-
"risk": "funds",
|
|
112
|
-
"summary": "Perp orders/leverage/withdraw with local signing (gated by review prompt).",
|
|
113
|
-
}
|
|
114
|
-
],
|
|
115
|
-
"gotchas": [
|
|
116
|
-
"Perp coins are mapped to asset_id (<10000).",
|
|
117
|
-
"Builder fees are attributed to 0xaA1D89f333857eD78F8434CC4f896A9293EFE65c and use tenths-of-bp units.",
|
|
118
|
-
"Prefer DRY_RUN=1 until ready to execute.",
|
|
119
|
-
],
|
|
120
|
-
}
|
|
121
|
-
return None
|
|
122
|
-
|
|
123
|
-
|
|
124
61
|
async def describe(
|
|
125
62
|
kind: Literal["adapter", "strategy"],
|
|
126
63
|
name: str,
|
|
127
64
|
include_manifest: bool = True,
|
|
128
65
|
include_readme_excerpt: bool = True,
|
|
129
|
-
include_claude_notes: bool = True,
|
|
130
66
|
) -> dict[str, Any]:
|
|
131
67
|
root = repo_root()
|
|
132
68
|
base = (
|
|
@@ -150,9 +86,4 @@ async def describe(
|
|
|
150
86
|
if readme:
|
|
151
87
|
out["readme_excerpt"] = readme
|
|
152
88
|
|
|
153
|
-
if include_claude_notes and kind == "adapter":
|
|
154
|
-
notes = _default_claude_notes_for_adapter(name)
|
|
155
|
-
if notes:
|
|
156
|
-
out["claude_notes"] = notes
|
|
157
|
-
|
|
158
89
|
return ok(out)
|
|
@@ -38,7 +38,7 @@ from wayfinder_paths.mcp.utils import (
|
|
|
38
38
|
|
|
39
39
|
class ExecutionRequest(BaseModel):
|
|
40
40
|
kind: Literal["swap", "send", "hyperliquid_deposit"]
|
|
41
|
-
wallet_label: str = Field(..., description="
|
|
41
|
+
wallet_label: str = Field(..., description="config.json wallet label (e.g. main)")
|
|
42
42
|
|
|
43
43
|
# Shared
|
|
44
44
|
amount: str = Field(..., description="Human units as a string (e.g. '1000')")
|
|
@@ -257,8 +257,10 @@ 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
|
|
261
|
-
all_quotes = quote_data.get("quotes",
|
|
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 = []
|
|
262
264
|
if isinstance(all_quotes, list):
|
|
263
265
|
result["providers"] = list(
|
|
264
266
|
{
|
|
@@ -439,7 +441,7 @@ async def execute(
|
|
|
439
441
|
if not sender or not pk:
|
|
440
442
|
response = err(
|
|
441
443
|
"invalid_wallet",
|
|
442
|
-
"Wallet must include address and private_key_hex in
|
|
444
|
+
"Wallet must include address and private_key_hex in config.json (local dev only)",
|
|
443
445
|
{"wallet_label": req.wallet_label},
|
|
444
446
|
)
|
|
445
447
|
store.put(key, tool_input, response)
|
|
@@ -530,6 +532,8 @@ async def execute(
|
|
|
530
532
|
swap_tx = dict(calldata)
|
|
531
533
|
swap_tx["chainId"] = int(from_chain_id)
|
|
532
534
|
swap_tx["from"] = to_checksum_address(sender)
|
|
535
|
+
if "value" in swap_tx:
|
|
536
|
+
swap_tx["value"] = int(swap_tx["value"])
|
|
533
537
|
|
|
534
538
|
token_addr = from_token_addr
|
|
535
539
|
spender = swap_tx.get("to")
|
|
@@ -382,7 +382,7 @@ async def hyperliquid_execute(
|
|
|
382
382
|
if not sender or not pk:
|
|
383
383
|
response = err(
|
|
384
384
|
"invalid_wallet",
|
|
385
|
-
"Wallet must include address and private_key_hex in
|
|
385
|
+
"Wallet must include address and private_key_hex in config.json (local dev only)",
|
|
386
386
|
{"wallet_label": want},
|
|
387
387
|
)
|
|
388
388
|
store.put(key, tool_input, response)
|
|
@@ -205,14 +205,13 @@ async def quote_swap(
|
|
|
205
205
|
except Exception as exc: # noqa: BLE001
|
|
206
206
|
return err("quote_error", str(exc))
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
if
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
quote_count = quotes_data.get("quote_count", len(all_quotes))
|
|
208
|
+
# API returns {"quotes": [...], "best_quote": {...}} at top level
|
|
209
|
+
raw_quotes = data.get("quotes", []) if isinstance(data, dict) else []
|
|
210
|
+
if not isinstance(raw_quotes, list):
|
|
211
|
+
raw_quotes = []
|
|
212
|
+
all_quotes = raw_quotes
|
|
213
|
+
best_quote = data.get("best_quote") if isinstance(data, dict) else None
|
|
214
|
+
quote_count = len(all_quotes)
|
|
216
215
|
|
|
217
216
|
providers: list[str] = []
|
|
218
217
|
seen: set[str] = set()
|
|
@@ -14,7 +14,6 @@ from wayfinder_paths.mcp.utils import (
|
|
|
14
14
|
normalize_address,
|
|
15
15
|
ok,
|
|
16
16
|
repo_root,
|
|
17
|
-
wallets_path,
|
|
18
17
|
)
|
|
19
18
|
|
|
20
19
|
PROTOCOL_ADAPTERS: dict[str, dict[str, Any]] = {
|
|
@@ -146,9 +145,7 @@ async def wallets(
|
|
|
146
145
|
parallel: bool = False,
|
|
147
146
|
include_zero_positions: bool = False,
|
|
148
147
|
) -> dict[str, Any]:
|
|
149
|
-
p = wallets_path()
|
|
150
148
|
root = repo_root()
|
|
151
|
-
rel = str(p.relative_to(root)) if p.is_absolute() and root in p.parents else str(p)
|
|
152
149
|
store = WalletProfileStore.default()
|
|
153
150
|
|
|
154
151
|
if action == "list":
|
|
@@ -164,7 +161,7 @@ async def wallets(
|
|
|
164
161
|
else:
|
|
165
162
|
view["protocols"] = []
|
|
166
163
|
wallet_list.append(view)
|
|
167
|
-
return ok({"
|
|
164
|
+
return ok({"config_path": "config.json", "wallets": wallet_list})
|
|
168
165
|
|
|
169
166
|
if action == "create":
|
|
170
167
|
existing = load_wallets()
|
|
@@ -178,7 +175,7 @@ async def wallets(
|
|
|
178
175
|
if str(w.get("label", "")).strip() == want:
|
|
179
176
|
return ok(
|
|
180
177
|
{
|
|
181
|
-
"
|
|
178
|
+
"config_path": "config.json",
|
|
182
179
|
"wallets": [_public_wallet_view(x) for x in existing],
|
|
183
180
|
"created": _public_wallet_view(w),
|
|
184
181
|
"note": "Wallet label already existed; returning existing wallet.",
|
|
@@ -187,12 +184,12 @@ async def wallets(
|
|
|
187
184
|
|
|
188
185
|
w = make_random_wallet()
|
|
189
186
|
w["label"] = want
|
|
190
|
-
write_wallet_to_json(w, out_dir=
|
|
187
|
+
write_wallet_to_json(w, out_dir=root, filename="config.json")
|
|
191
188
|
|
|
192
189
|
refreshed = load_wallets()
|
|
193
190
|
return ok(
|
|
194
191
|
{
|
|
195
|
-
"
|
|
192
|
+
"config_path": "config.json",
|
|
196
193
|
"wallets": [_public_wallet_view(x) for x in refreshed],
|
|
197
194
|
"created": _public_wallet_view(w),
|
|
198
195
|
}
|
wayfinder_paths/mcp/utils.py
CHANGED
|
@@ -64,32 +64,10 @@ def load_config_json() -> dict[str, Any]:
|
|
|
64
64
|
return parsed if isinstance(parsed, dict) else {}
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def wallets_path() -> Path:
|
|
68
|
-
cfg = load_config_json()
|
|
69
|
-
system = cfg.get("system") if isinstance(cfg.get("system"), dict) else {}
|
|
70
|
-
candidate = (
|
|
71
|
-
(system or {}).get("wallets_path")
|
|
72
|
-
or os.getenv("WALLETS_PATH")
|
|
73
|
-
or "wallets.json"
|
|
74
|
-
)
|
|
75
|
-
p = Path(candidate)
|
|
76
|
-
if not p.is_absolute():
|
|
77
|
-
p = repo_root() / p
|
|
78
|
-
return p
|
|
79
|
-
|
|
80
|
-
|
|
81
67
|
def load_wallets() -> list[dict[str, Any]]:
|
|
82
68
|
cfg = load_config_json()
|
|
83
69
|
if isinstance(cfg.get("wallets"), list):
|
|
84
70
|
return [w for w in cfg["wallets"] if isinstance(w, dict)]
|
|
85
|
-
|
|
86
|
-
p = wallets_path()
|
|
87
|
-
if p.exists():
|
|
88
|
-
parsed = load_json_file(p)
|
|
89
|
-
if isinstance(parsed, list):
|
|
90
|
-
return [w for w in parsed if isinstance(w, dict)]
|
|
91
|
-
if isinstance(parsed, dict) and isinstance(parsed.get("wallets"), list):
|
|
92
|
-
return [w for w in parsed["wallets"] if isinstance(w, dict)]
|
|
93
71
|
return []
|
|
94
72
|
|
|
95
73
|
|
wayfinder_paths/policies/lifi.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
2
|
+
LIFI_GENERIC,
|
|
3
|
+
LIFI_ROUTER_HYPEREVM,
|
|
4
|
+
)
|
|
1
5
|
from wayfinder_paths.policies.util import allow_functions
|
|
2
6
|
|
|
3
|
-
LIFI_ROUTERS: dict[int:str] = {999:
|
|
4
|
-
LIFI_GENERIC = "0x31a9b1835864706Af10103b31Ea2b79bdb995F5F"
|
|
7
|
+
LIFI_ROUTERS: dict[int:str] = {999: LIFI_ROUTER_HYPEREVM}
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
async def lifi_swap(chain_id):
|
|
@@ -5,6 +5,7 @@ from wayfinder_paths.core.constants.contracts import (
|
|
|
5
5
|
MOONWELL_M_WETH,
|
|
6
6
|
MOONWELL_M_WSTETH,
|
|
7
7
|
)
|
|
8
|
+
from wayfinder_paths.core.constants.moonwell_abi import COMPTROLLER_ABI
|
|
8
9
|
from wayfinder_paths.policies.util import allow_functions
|
|
9
10
|
|
|
10
11
|
WETH = BASE_WETH
|
|
@@ -52,8 +53,9 @@ async def mwsteth_approve_or_mint_or_redeem():
|
|
|
52
53
|
|
|
53
54
|
async def moonwell_comptroller_enter_markets_or_claim_rewards():
|
|
54
55
|
return await allow_functions(
|
|
55
|
-
policy_name="Allow
|
|
56
|
+
policy_name="Allow lend and claimReward",
|
|
56
57
|
abi_chain_id=8453,
|
|
57
58
|
address=COMPTROLLER,
|
|
58
59
|
function_names=["enterMarkets", "claimReward"],
|
|
60
|
+
manual_abi=COMPTROLLER_ABI,
|
|
59
61
|
)
|
wayfinder_paths/policies/util.py
CHANGED
|
@@ -6,7 +6,8 @@ async def allow_functions(
|
|
|
6
6
|
abi_chain_id: int,
|
|
7
7
|
address: str,
|
|
8
8
|
function_names: list[str],
|
|
9
|
-
abi_address_override=None,
|
|
9
|
+
abi_address_override: str = None,
|
|
10
|
+
manual_abi: dict = None,
|
|
10
11
|
):
|
|
11
12
|
# Note: ChainID is just for fetching ABI, doesn't appear in the final policy. Doesn't bind a strict chain.
|
|
12
13
|
return {
|
|
@@ -23,7 +24,8 @@ async def allow_functions(
|
|
|
23
24
|
{
|
|
24
25
|
"field_source": "ethereum_calldata",
|
|
25
26
|
"field": "function_name",
|
|
26
|
-
"abi":
|
|
27
|
+
"abi": manual_abi
|
|
28
|
+
or await get_abi_filtered(
|
|
27
29
|
abi_chain_id, abi_address_override or address, function_names
|
|
28
30
|
),
|
|
29
31
|
"operator": "in",
|
|
@@ -48,7 +48,7 @@ from wayfinder_paths.core.analytics import (
|
|
|
48
48
|
from wayfinder_paths.core.analytics import (
|
|
49
49
|
z_from_conf as analytics_z_from_conf,
|
|
50
50
|
)
|
|
51
|
-
from wayfinder_paths.core.constants.contracts import HYPERLIQUID_BRIDGE
|
|
51
|
+
from wayfinder_paths.core.constants.contracts import HYPE_FEE_WALLET, HYPERLIQUID_BRIDGE
|
|
52
52
|
from wayfinder_paths.core.strategies.descriptors import (
|
|
53
53
|
Complexity,
|
|
54
54
|
Directionality,
|
|
@@ -111,7 +111,6 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
111
111
|
# Rotation cooldown
|
|
112
112
|
ROTATION_MIN_INTERVAL_DAYS = 14
|
|
113
113
|
|
|
114
|
-
HYPE_FEE_WALLET: str = "0xaA1D89f333857eD78F8434CC4f896A9293EFE65c"
|
|
115
114
|
HYPE_PRO_FEE: int = 30
|
|
116
115
|
DEFAULT_BUILDER_FEE: dict[str, Any] = {"b": HYPE_FEE_WALLET, "f": HYPE_PRO_FEE}
|
|
117
116
|
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
2
|
+
HYPE_OFT_ADDRESS,
|
|
3
|
+
KHYPE_ADDRESS,
|
|
4
|
+
KHYPE_STAKING_ACCOUNTANT,
|
|
5
|
+
LHYPE_ACCOUNTANT,
|
|
6
|
+
LOOPED_HYPE_ADDRESS,
|
|
7
|
+
)
|
|
8
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
9
|
+
HYPEREVM_WHYPE as WHYPE_ADDRESS,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# Re-export addresses for use by strategy modules
|
|
13
|
+
__all__ = [
|
|
14
|
+
"HYPE_OFT_ADDRESS",
|
|
15
|
+
"WHYPE_ADDRESS",
|
|
16
|
+
"KHYPE_ADDRESS",
|
|
17
|
+
"KHYPE_STAKING_ACCOUNTANT",
|
|
18
|
+
"LHYPE_ACCOUNTANT",
|
|
19
|
+
"LOOPED_HYPE_ADDRESS",
|
|
20
|
+
]
|
|
21
|
+
|
|
1
22
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
2
23
|
# TOKEN IDS (wayfinder token identifiers)
|
|
3
24
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -14,20 +35,6 @@ KHYPE_LST = "kinetic-staked-hype-hyperevm"
|
|
|
14
35
|
LOOPED_HYPE = "looped-hype-hyperevm"
|
|
15
36
|
USDC_HYPE = "usd-coin-hyperevm"
|
|
16
37
|
|
|
17
|
-
|
|
18
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
-
# CONTRACT ADDRESSES
|
|
20
|
-
# ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
# HyperEVM token addresses
|
|
23
|
-
KHYPE_ADDRESS = "0xfD739d4e423301CE9385c1fb8850539D657C296D"
|
|
24
|
-
LOOPED_HYPE_ADDRESS = "0x5748ae796AE46A4F1348a1693de4b50560485562"
|
|
25
|
-
WHYPE_ADDRESS = "0x5555555555555555555555555555555555555555"
|
|
26
|
-
|
|
27
|
-
# Accountant contracts for exchange rate calculations
|
|
28
|
-
KHYPE_STAKING_ACCOUNTANT = "0x9209648Ec9D448EF57116B73A2f081835643dc7A"
|
|
29
|
-
LHYPE_ACCOUNTANT = "0xcE621a3CA6F72706678cFF0572ae8d15e5F001c3"
|
|
30
|
-
|
|
31
38
|
# Chain IDs
|
|
32
39
|
HYPEREVM_CHAIN_ID = 999
|
|
33
40
|
ARBITRUM_CHAIN_ID = 42161
|
|
@@ -100,8 +107,8 @@ BOROS_MIN_DEPOSIT_HYPE = 0.4
|
|
|
100
107
|
BOROS_MIN_TENOR_DAYS = 3 # Roll to new market if < 3 days to expiry
|
|
101
108
|
BOROS_ENABLE_MIN_TOTAL_USD = 80.0 # Skip Boros if capital below this
|
|
102
109
|
|
|
103
|
-
# LayerZero OFT bridge (HyperEVM native HYPE
|
|
104
|
-
HYPE_OFT_ADDRESS
|
|
110
|
+
# LayerZero OFT bridge (HyperEVM native HYPE -> Arbitrum OFT HYPE)
|
|
111
|
+
# HYPE_OFT_ADDRESS imported from contracts.py
|
|
105
112
|
LZ_EID_ARBITRUM = 30110
|
|
106
113
|
|
|
107
114
|
# Minimal IOFT ABI for quoting + sending.
|
|
@@ -23,7 +23,6 @@ from loguru import logger
|
|
|
23
23
|
|
|
24
24
|
from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
|
|
25
25
|
from wayfinder_paths.adapters.boros_adapter import BorosAdapter, BorosMarketQuote
|
|
26
|
-
from wayfinder_paths.adapters.boros_adapter.client import BorosClient
|
|
27
26
|
from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
|
|
28
27
|
from wayfinder_paths.adapters.hyperliquid_adapter.adapter import (
|
|
29
28
|
HyperliquidAdapter,
|
|
@@ -273,75 +272,37 @@ class BorosHypeStrategy(
|
|
|
273
272
|
user_address=user_address,
|
|
274
273
|
)
|
|
275
274
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
logger.warning(f"Boros adapter connection failed: {e}")
|
|
282
|
-
if not connected:
|
|
283
|
-
logger.warning(
|
|
284
|
-
"Boros adapter connection failed - some features may not work"
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
# Initialize Hyperliquid adapter for market data and perp trading
|
|
288
|
-
# The adapter will create Exchange internally with local signer from config
|
|
289
|
-
# if no sign_callback is provided (and not in simulation mode).
|
|
290
|
-
try:
|
|
291
|
-
self.hyperliquid_adapter = HyperliquidAdapter(
|
|
292
|
-
config=self._config,
|
|
293
|
-
simulation=self.simulation,
|
|
294
|
-
sign_callback=self.strategy_sign_typed_data,
|
|
295
|
-
)
|
|
296
|
-
try:
|
|
297
|
-
hl_connected = await self.hyperliquid_adapter.connect()
|
|
298
|
-
except Exception as e:
|
|
299
|
-
hl_connected = False
|
|
300
|
-
logger.warning(f"Hyperliquid adapter connection failed: {e}")
|
|
301
|
-
if not hl_connected:
|
|
302
|
-
logger.warning("Hyperliquid adapter connection failed")
|
|
303
|
-
except Exception as e:
|
|
304
|
-
logger.warning(f"Hyperliquid adapter not available: {e}")
|
|
305
|
-
self.hyperliquid_adapter = None
|
|
306
|
-
|
|
307
|
-
# Initialize BalanceAdapter for on-chain balance reads
|
|
308
|
-
try:
|
|
309
|
-
self.balance_adapter = BalanceAdapter(
|
|
310
|
-
config=self._config,
|
|
311
|
-
main_wallet_signing_callback=main_sign_callback,
|
|
312
|
-
strategy_wallet_signing_callback=self._sign_callback,
|
|
313
|
-
)
|
|
314
|
-
logger.debug("BalanceAdapter initialized for on-chain balance reads")
|
|
315
|
-
except Exception as e:
|
|
316
|
-
logger.warning(f"BalanceAdapter initialization failed: {e}")
|
|
317
|
-
self.balance_adapter = None
|
|
275
|
+
self.hyperliquid_adapter = HyperliquidAdapter(
|
|
276
|
+
config=self._config,
|
|
277
|
+
simulation=self.simulation,
|
|
278
|
+
sign_callback=self.strategy_sign_typed_data,
|
|
279
|
+
)
|
|
318
280
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
self.brap_adapter = None
|
|
281
|
+
self.balance_adapter = BalanceAdapter(
|
|
282
|
+
config=self._config,
|
|
283
|
+
main_wallet_signing_callback=main_sign_callback,
|
|
284
|
+
strategy_wallet_signing_callback=self._sign_callback,
|
|
285
|
+
)
|
|
286
|
+
self.brap_adapter = BRAPAdapter(
|
|
287
|
+
config=self._config,
|
|
288
|
+
strategy_wallet_signing_callback=self._sign_callback,
|
|
289
|
+
)
|
|
329
290
|
|
|
330
|
-
|
|
331
|
-
try:
|
|
332
|
-
self.ledger_adapter = LedgerAdapter()
|
|
333
|
-
logger.debug("LedgerAdapter initialized")
|
|
334
|
-
except Exception as e:
|
|
335
|
-
logger.warning(f"LedgerAdapter initialization failed: {e}")
|
|
336
|
-
self.ledger_adapter = None
|
|
291
|
+
self.ledger_adapter = LedgerAdapter()
|
|
337
292
|
|
|
338
293
|
logger.info(f"BorosHypeStrategy setup complete (simulation={self.simulation})")
|
|
339
294
|
|
|
340
295
|
async def analyze(self, deposit_usdc: float = 1000.0) -> dict[str, Any]:
|
|
341
296
|
# Read-only market analysis returning Boros fixed-rate markets for HYPE
|
|
342
|
-
client
|
|
343
|
-
|
|
344
|
-
|
|
297
|
+
# Client ownership: BorosAdapter owns the client; we require adapter to be set up
|
|
298
|
+
if not self.boros_adapter:
|
|
299
|
+
return {
|
|
300
|
+
"success": False,
|
|
301
|
+
"error": "BorosAdapter not initialized - call setup() first",
|
|
302
|
+
"deposit_usdc": float(deposit_usdc),
|
|
303
|
+
"hype_markets": [],
|
|
304
|
+
}
|
|
305
|
+
client = self.boros_adapter.boros_client
|
|
345
306
|
try:
|
|
346
307
|
markets = await client.list_markets(is_whitelisted=True, skip=0, limit=250)
|
|
347
308
|
except Exception as exc: # noqa: BLE001
|
|
@@ -12,6 +12,7 @@ from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
|
|
|
12
12
|
from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
13
13
|
from wayfinder_paths.adapters.pool_adapter.adapter import PoolAdapter
|
|
14
14
|
from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
|
|
15
|
+
from wayfinder_paths.core.constants.contracts import ENSO_ROUTER
|
|
15
16
|
from wayfinder_paths.core.strategies.descriptors import (
|
|
16
17
|
Complexity,
|
|
17
18
|
Directionality,
|
|
@@ -1591,7 +1592,7 @@ class StablecoinYieldStrategy(Strategy):
|
|
|
1591
1592
|
|
|
1592
1593
|
@staticmethod
|
|
1593
1594
|
def policies() -> list[str]:
|
|
1594
|
-
enso_router =
|
|
1595
|
+
enso_router = ENSO_ROUTER.lower()
|
|
1595
1596
|
approve_enso = (
|
|
1596
1597
|
"eth.tx.data[0..10] == '0x095ea7b3' && "
|
|
1597
1598
|
f"eth.tx.data[34..74] == '{enso_router[2:]}'"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: wayfinder-paths
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.29
|
|
4
4
|
Summary: Wayfinder Path: strategies and adapters
|
|
5
5
|
Author: Wayfinder
|
|
6
6
|
Author-email: dev@wayfinder.ai
|
|
7
7
|
Requires-Python: >=3.12,<4.0
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
11
|
Requires-Dist: aiocache (>=0.12.3,<0.13.0)
|
|
11
12
|
Requires-Dist: aiohttp (>=3.13.0,<4.0.0)
|
|
12
13
|
Requires-Dist: eth-account (>=0.13.7,<0.14.0)
|