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
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
import re
|
|
5
|
-
import shutil
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from wayfinder_paths.core.utils.wallets import make_random_wallet, write_wallet_to_json
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def sanitize_name(name: str) -> str:
|
|
12
|
-
# Replace spaces and special chars with underscores, lowercase
|
|
13
|
-
name = re.sub(r"[^a-zA-Z0-9_-]", "_", name)
|
|
14
|
-
name = re.sub(r"_+", "_", name)
|
|
15
|
-
name = name.strip("_")
|
|
16
|
-
return name.lower()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def update_strategy_file(strategy_path: Path, class_name: str) -> None:
|
|
20
|
-
content = strategy_path.read_text()
|
|
21
|
-
# Replace MyStrategy with the new class name
|
|
22
|
-
content = content.replace("MyStrategy", class_name)
|
|
23
|
-
# Replace my_strategy references in docstrings/comments
|
|
24
|
-
content = re.sub(
|
|
25
|
-
r"my_strategy", class_name.lower().replace("Strategy", ""), content
|
|
26
|
-
)
|
|
27
|
-
strategy_path.write_text(content)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def main():
|
|
31
|
-
parser = argparse.ArgumentParser(
|
|
32
|
-
description="Create a new strategy from template with dedicated wallet"
|
|
33
|
-
)
|
|
34
|
-
parser.add_argument(
|
|
35
|
-
"name",
|
|
36
|
-
help="Strategy name (e.g., 'my_awesome_strategy' or 'My Awesome Strategy')",
|
|
37
|
-
)
|
|
38
|
-
parser.add_argument(
|
|
39
|
-
"--template-dir",
|
|
40
|
-
type=Path,
|
|
41
|
-
default=Path(__file__).parent.parent / "templates" / "strategy",
|
|
42
|
-
help="Path to strategy template directory",
|
|
43
|
-
)
|
|
44
|
-
parser.add_argument(
|
|
45
|
-
"--strategies-dir",
|
|
46
|
-
type=Path,
|
|
47
|
-
default=Path(__file__).parent.parent / "strategies",
|
|
48
|
-
help="Path to strategies directory",
|
|
49
|
-
)
|
|
50
|
-
parser.add_argument(
|
|
51
|
-
"--wallets-file",
|
|
52
|
-
type=Path,
|
|
53
|
-
default=Path(__file__).parent.parent.parent / "config.json",
|
|
54
|
-
help="Path to config.json file",
|
|
55
|
-
)
|
|
56
|
-
parser.add_argument(
|
|
57
|
-
"--override",
|
|
58
|
-
action="store_true",
|
|
59
|
-
help="Override existing strategy directory if it exists",
|
|
60
|
-
)
|
|
61
|
-
args = parser.parse_args()
|
|
62
|
-
|
|
63
|
-
# Sanitize name for directory
|
|
64
|
-
dir_name = sanitize_name(args.name)
|
|
65
|
-
strategy_dir = args.strategies_dir / dir_name
|
|
66
|
-
|
|
67
|
-
if strategy_dir.exists() and not args.override:
|
|
68
|
-
raise SystemExit(
|
|
69
|
-
f"Strategy directory already exists: {strategy_dir}\n"
|
|
70
|
-
"Use --override to replace it"
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
if not args.template_dir.exists():
|
|
74
|
-
raise SystemExit(f"Template directory not found: {args.template_dir}")
|
|
75
|
-
|
|
76
|
-
if strategy_dir.exists():
|
|
77
|
-
print(f"Removing existing directory: {strategy_dir}")
|
|
78
|
-
shutil.rmtree(strategy_dir)
|
|
79
|
-
strategy_dir.mkdir(parents=True, exist_ok=True)
|
|
80
|
-
print(f"Created strategy directory: {strategy_dir}")
|
|
81
|
-
|
|
82
|
-
# Copy template files
|
|
83
|
-
template_files = [
|
|
84
|
-
"strategy.py",
|
|
85
|
-
"test_strategy.py",
|
|
86
|
-
"examples.json",
|
|
87
|
-
"README.md",
|
|
88
|
-
]
|
|
89
|
-
for filename in template_files:
|
|
90
|
-
src = args.template_dir / filename
|
|
91
|
-
if src.exists():
|
|
92
|
-
dst = strategy_dir / filename
|
|
93
|
-
shutil.copy2(src, dst)
|
|
94
|
-
print(f" Copied {filename}")
|
|
95
|
-
|
|
96
|
-
# Generate class name from strategy name
|
|
97
|
-
class_name = "".join(word.capitalize() for word in dir_name.split("_"))
|
|
98
|
-
if not class_name.endswith("Strategy"):
|
|
99
|
-
class_name += "Strategy"
|
|
100
|
-
|
|
101
|
-
strategy_file = strategy_dir / "strategy.py"
|
|
102
|
-
if strategy_file.exists():
|
|
103
|
-
update_strategy_file(strategy_file, class_name)
|
|
104
|
-
print(f" Updated strategy.py with class name: {class_name}")
|
|
105
|
-
|
|
106
|
-
# Generate wallet with label matching directory name (strategy identifier)
|
|
107
|
-
# If config.json doesn't exist, create it with a main wallet first
|
|
108
|
-
if not args.wallets_file.exists():
|
|
109
|
-
print(" Creating new config.json with main wallet...")
|
|
110
|
-
main_wallet = make_random_wallet()
|
|
111
|
-
main_wallet["label"] = "main"
|
|
112
|
-
write_wallet_to_json(
|
|
113
|
-
main_wallet,
|
|
114
|
-
out_dir=args.wallets_file.parent,
|
|
115
|
-
filename=args.wallets_file.name,
|
|
116
|
-
)
|
|
117
|
-
print(f" Generated main wallet: {main_wallet['address']}")
|
|
118
|
-
|
|
119
|
-
# Generate strategy wallet (will append to existing config.json)
|
|
120
|
-
wallet = make_random_wallet()
|
|
121
|
-
wallet["label"] = dir_name
|
|
122
|
-
write_wallet_to_json(
|
|
123
|
-
wallet, out_dir=args.wallets_file.parent, filename=args.wallets_file.name
|
|
124
|
-
)
|
|
125
|
-
print(f" Generated strategy wallet: {wallet['address']} (label: {dir_name})")
|
|
126
|
-
|
|
127
|
-
print("\n✅ Strategy created successfully!")
|
|
128
|
-
print(f" Directory: {strategy_dir}")
|
|
129
|
-
print(f" Name: {dir_name}")
|
|
130
|
-
print(f" Class: {class_name}")
|
|
131
|
-
print(f" Wallet: {wallet['address']}")
|
|
132
|
-
print("\nNext steps:")
|
|
133
|
-
print(f" 1. Edit {strategy_dir / 'strategy.py'} to implement your strategy")
|
|
134
|
-
print(" 2. Add required adapters in __init__")
|
|
135
|
-
print(f" 3. Test with: just test-strategy {dir_name}")
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if __name__ == "__main__":
|
|
139
|
-
main()
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import json
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from eth_account import Account
|
|
6
|
-
|
|
7
|
-
from wayfinder_paths.core.utils.wallets import (
|
|
8
|
-
load_wallets,
|
|
9
|
-
make_random_wallet,
|
|
10
|
-
write_wallet_to_json,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def to_keystore_json(private_key_hex: str, password: str):
|
|
15
|
-
return Account.encrypt(private_key_hex, password)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def main():
|
|
19
|
-
parser = argparse.ArgumentParser(description="Generate local dev wallets")
|
|
20
|
-
parser.add_argument(
|
|
21
|
-
"-n",
|
|
22
|
-
type=int,
|
|
23
|
-
default=0,
|
|
24
|
-
help="Number of wallets to create (ignored if --label is used)",
|
|
25
|
-
)
|
|
26
|
-
parser.add_argument(
|
|
27
|
-
"--out-dir",
|
|
28
|
-
type=Path,
|
|
29
|
-
default=Path("."),
|
|
30
|
-
help="Output directory for config.json (and keystore files)",
|
|
31
|
-
)
|
|
32
|
-
parser.add_argument(
|
|
33
|
-
"--keystore-password",
|
|
34
|
-
type=str,
|
|
35
|
-
default=None,
|
|
36
|
-
help="Optional password to write geth-compatible keystores",
|
|
37
|
-
)
|
|
38
|
-
parser.add_argument(
|
|
39
|
-
"--label",
|
|
40
|
-
type=str,
|
|
41
|
-
default=None,
|
|
42
|
-
help="Create a wallet with a custom label (e.g., strategy name). If not provided, auto-generates labels.",
|
|
43
|
-
)
|
|
44
|
-
parser.add_argument(
|
|
45
|
-
"--default",
|
|
46
|
-
action="store_true",
|
|
47
|
-
help="Create a default 'main' wallet if none exists (used by CI)",
|
|
48
|
-
)
|
|
49
|
-
args = parser.parse_args()
|
|
50
|
-
|
|
51
|
-
# --default is equivalent to -n 1 (create main wallet if needed)
|
|
52
|
-
if args.default and args.n == 0 and not args.label:
|
|
53
|
-
args.n = 1
|
|
54
|
-
|
|
55
|
-
args.out_dir.mkdir(parents=True, exist_ok=True)
|
|
56
|
-
|
|
57
|
-
existing = load_wallets(args.out_dir, "config.json")
|
|
58
|
-
has_main = any(w.get("label") in ("main", "default") for w in existing)
|
|
59
|
-
|
|
60
|
-
rows: list[dict[str, str]] = []
|
|
61
|
-
index = 0
|
|
62
|
-
|
|
63
|
-
# Custom labeled wallet (e.g., for strategy name)
|
|
64
|
-
if args.label:
|
|
65
|
-
# Check if label already exists - if so, skip (don't create duplicate)
|
|
66
|
-
if any(w.get("label") == args.label for w in existing):
|
|
67
|
-
print(f"Wallet with label '{args.label}' already exists, skipping...")
|
|
68
|
-
else:
|
|
69
|
-
w = make_random_wallet()
|
|
70
|
-
w["label"] = args.label
|
|
71
|
-
rows.append(w)
|
|
72
|
-
print(f"[{index}] {w['address']} (label: {args.label})")
|
|
73
|
-
write_wallet_to_json(w, out_dir=args.out_dir, filename="config.json")
|
|
74
|
-
if args.keystore_password:
|
|
75
|
-
ks = to_keystore_json(w["private_key_hex"], args.keystore_password)
|
|
76
|
-
ks_path = args.out_dir / f"keystore_{w['address']}.json"
|
|
77
|
-
ks_path.write_text(json.dumps(ks))
|
|
78
|
-
index += 1
|
|
79
|
-
|
|
80
|
-
# If no wallets existed before, also create a "main" wallet
|
|
81
|
-
if not existing:
|
|
82
|
-
main_w = make_random_wallet()
|
|
83
|
-
main_w["label"] = "main"
|
|
84
|
-
rows.append(main_w)
|
|
85
|
-
print(f"[{index}] {main_w['address']} (main)")
|
|
86
|
-
write_wallet_to_json(
|
|
87
|
-
main_w, out_dir=args.out_dir, filename="config.json"
|
|
88
|
-
)
|
|
89
|
-
if args.keystore_password:
|
|
90
|
-
ks = to_keystore_json(
|
|
91
|
-
main_w["private_key_hex"], args.keystore_password
|
|
92
|
-
)
|
|
93
|
-
ks_path = args.out_dir / f"keystore_{main_w['address']}.json"
|
|
94
|
-
ks_path.write_text(json.dumps(ks))
|
|
95
|
-
index += 1
|
|
96
|
-
else:
|
|
97
|
-
if args.n == 0:
|
|
98
|
-
args.n = 1
|
|
99
|
-
|
|
100
|
-
# Find next temporary number
|
|
101
|
-
existing_labels = {
|
|
102
|
-
w.get("label", "")
|
|
103
|
-
for w in existing
|
|
104
|
-
if w.get("label", "").startswith("temporary_")
|
|
105
|
-
}
|
|
106
|
-
temp_numbers = set()
|
|
107
|
-
for label in existing_labels:
|
|
108
|
-
try:
|
|
109
|
-
num = int(label.replace("temporary_", ""))
|
|
110
|
-
temp_numbers.add(num)
|
|
111
|
-
except ValueError:
|
|
112
|
-
pass
|
|
113
|
-
next_temp_num = 1
|
|
114
|
-
if temp_numbers:
|
|
115
|
-
next_temp_num = max(temp_numbers) + 1
|
|
116
|
-
|
|
117
|
-
for i in range(args.n):
|
|
118
|
-
w = make_random_wallet()
|
|
119
|
-
# Label first wallet as "main" if main doesn't exist, otherwise use temporary_N
|
|
120
|
-
if i == 0 and not has_main:
|
|
121
|
-
w["label"] = "main"
|
|
122
|
-
rows.append(w)
|
|
123
|
-
print(f"[{index}] {w['address']} (main)")
|
|
124
|
-
else:
|
|
125
|
-
# Find next available temporary number
|
|
126
|
-
while next_temp_num in temp_numbers:
|
|
127
|
-
next_temp_num += 1
|
|
128
|
-
w["label"] = f"temporary_{next_temp_num}"
|
|
129
|
-
temp_numbers.add(next_temp_num)
|
|
130
|
-
rows.append(w)
|
|
131
|
-
print(f"[{index}] {w['address']} (label: temporary_{next_temp_num})")
|
|
132
|
-
|
|
133
|
-
write_wallet_to_json(w, out_dir=args.out_dir, filename="config.json")
|
|
134
|
-
if args.keystore_password:
|
|
135
|
-
ks = to_keystore_json(w["private_key_hex"], args.keystore_password)
|
|
136
|
-
ks_path = args.out_dir / f"keystore_{w['address']}.json"
|
|
137
|
-
ks_path.write_text(json.dumps(ks))
|
|
138
|
-
index += 1
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if __name__ == "__main__":
|
|
142
|
-
main()
|
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: wayfinder-paths
|
|
3
|
-
Version: 0.1.23
|
|
4
|
-
Summary: Wayfinder Path: strategies and adapters
|
|
5
|
-
Author: Wayfinder
|
|
6
|
-
Author-email: dev@wayfinder.ai
|
|
7
|
-
Requires-Python: >=3.12,<4.0
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
-
Requires-Dist: aiohttp (>=3.13.0,<4.0.0)
|
|
11
|
-
Requires-Dist: eth-account (>=0.13.7,<0.14.0)
|
|
12
|
-
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
13
|
-
Requires-Dist: hyperliquid-felix
|
|
14
|
-
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
15
|
-
Requires-Dist: numpy (>=1.26.0,<2.0.0)
|
|
16
|
-
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
17
|
-
Requires-Dist: pydantic (>=2.11.9,<3.0.0)
|
|
18
|
-
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
19
|
-
Requires-Dist: web3 (>=7.13.0,<8.0.0)
|
|
20
|
-
Description-Content-Type: text/markdown
|
|
21
|
-
|
|
22
|
-
# Wayfinder Paths
|
|
23
|
-
|
|
24
|
-
[](https://www.python.org/downloads/)
|
|
25
|
-
[](https://pypi.org/project/wayfinder-paths/)
|
|
26
|
-
[](https://discord.gg/fUVwGMXjm3)
|
|
27
|
-
|
|
28
|
-
Open-source framework for building automated crypto trading strategies and protocol integrations. Develop, test, and deploy strategies with direct wallet integration across multiple chains.
|
|
29
|
-
|
|
30
|
-
## Quick Start
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
# Clone the repository
|
|
34
|
-
git clone https://github.com/WayfinderFoundation/wayfinder-paths.git
|
|
35
|
-
cd wayfinder-paths
|
|
36
|
-
|
|
37
|
-
# Install Poetry if needed
|
|
38
|
-
curl -sSL https://install.python-poetry.org | python3 -
|
|
39
|
-
|
|
40
|
-
# Install dependencies
|
|
41
|
-
poetry install
|
|
42
|
-
|
|
43
|
-
# Generate test wallets (creates config.json with main wallet)
|
|
44
|
-
just create-wallets
|
|
45
|
-
# Or: poetry run python wayfinder_paths/scripts/make_wallets.py -n 1
|
|
46
|
-
|
|
47
|
-
# Create a strategy-specific wallet
|
|
48
|
-
just create-wallet stablecoin_yield_strategy
|
|
49
|
-
# Or: poetry run python wayfinder_paths/scripts/make_wallets.py --label stablecoin_yield_strategy
|
|
50
|
-
|
|
51
|
-
# Add your API key to config.json under system.api_key
|
|
52
|
-
|
|
53
|
-
# Run a strategy
|
|
54
|
-
poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --action status --config config.json
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Repository Structure
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
wayfinder-paths/
|
|
61
|
-
├── wayfinder_paths/
|
|
62
|
-
│ ├── core/ # Core framework (maintained by team)
|
|
63
|
-
│ │ ├── adapters/ # BaseAdapter class
|
|
64
|
-
│ │ ├── clients/ # API clients (Token, Wallet, Pool, BRAP, Ledger, etc.)
|
|
65
|
-
│ │ ├── engine/ # StrategyJob execution engine
|
|
66
|
-
│ │ ├── strategies/ # Strategy base class and descriptors
|
|
67
|
-
│ │ ├── utils/ # Web3, EVM helpers, transaction utilities
|
|
68
|
-
│ │ └── config.py # Configuration system
|
|
69
|
-
│ ├── adapters/ # Protocol integrations (community contributions)
|
|
70
|
-
│ │ ├── balance_adapter/ # Wallet/token balances and transfers
|
|
71
|
-
│ │ ├── brap_adapter/ # Cross-chain swaps and bridges
|
|
72
|
-
│ │ ├── ledger_adapter/ # Transaction recording
|
|
73
|
-
│ │ ├── moonwell_adapter/ # Moonwell lending protocol
|
|
74
|
-
│ │ ├── pool_adapter/ # DeFi pool data
|
|
75
|
-
│ │ └── token_adapter/ # Token metadata and prices
|
|
76
|
-
│ ├── strategies/ # Trading strategies (community contributions)
|
|
77
|
-
│ │ ├── basis_trading_strategy/
|
|
78
|
-
│ │ ├── hyperlend_stable_yield_strategy/
|
|
79
|
-
│ │ ├── moonwell_wsteth_loop_strategy/
|
|
80
|
-
│ │ └── stablecoin_yield_strategy/
|
|
81
|
-
│ ├── templates/ # Starter templates
|
|
82
|
-
│ │ ├── adapter/
|
|
83
|
-
│ │ └── strategy/
|
|
84
|
-
│ ├── scripts/ # Utility scripts
|
|
85
|
-
│ └── run_strategy.py # CLI entry point
|
|
86
|
-
├── config.json # Local config (not committed)
|
|
87
|
-
├── pyproject.toml # Project dependencies
|
|
88
|
-
└── README.md
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Architecture
|
|
92
|
-
|
|
93
|
-
### Layered Design
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
Strategy Layer - Trading logic (deposit, update, withdraw, exit)
|
|
97
|
-
↓
|
|
98
|
-
Adapter Layer - Protocol integrations (BalanceAdapter, PoolAdapter, etc.)
|
|
99
|
-
↓
|
|
100
|
-
Client Layer - API wrappers (TokenClient, PoolClient, LedgerClient, etc.)
|
|
101
|
-
↓
|
|
102
|
-
Network - RPCs, Wayfinder API, external services
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
**Key principle**: Strategies call adapters, adapters compose clients, clients handle network communication.
|
|
106
|
-
|
|
107
|
-
### Strategies
|
|
108
|
-
|
|
109
|
-
Strategies implement trading logic by extending the `Strategy` base class:
|
|
110
|
-
|
|
111
|
-
```python
|
|
112
|
-
from wayfinder_paths.core.strategies.Strategy import Strategy, StatusDict, StatusTuple
|
|
113
|
-
|
|
114
|
-
class MyStrategy(Strategy):
|
|
115
|
-
name = "My Strategy"
|
|
116
|
-
|
|
117
|
-
def __init__(self, config=None, **kwargs):
|
|
118
|
-
super().__init__(config, **kwargs)
|
|
119
|
-
# Register adapters
|
|
120
|
-
balance_adapter = BalanceAdapter(config, **kwargs)
|
|
121
|
-
self.register_adapters([balance_adapter])
|
|
122
|
-
self.balance_adapter = balance_adapter
|
|
123
|
-
|
|
124
|
-
async def deposit(self, main_token_amount=0.0, gas_token_amount=0.0) -> StatusTuple:
|
|
125
|
-
"""Move funds from main wallet into strategy wallet."""
|
|
126
|
-
return (True, "Deposited successfully")
|
|
127
|
-
|
|
128
|
-
async def update(self) -> StatusTuple:
|
|
129
|
-
"""Rebalance or optimize positions."""
|
|
130
|
-
return (True, "Updated successfully")
|
|
131
|
-
|
|
132
|
-
async def exit(self, **kwargs) -> StatusTuple:
|
|
133
|
-
"""Transfer funds from strategy wallet back to main wallet."""
|
|
134
|
-
return (True, "Exited successfully")
|
|
135
|
-
|
|
136
|
-
async def _status(self) -> StatusDict:
|
|
137
|
-
"""Report current state."""
|
|
138
|
-
return {
|
|
139
|
-
"portfolio_value": 0.0,
|
|
140
|
-
"net_deposit": 0.0,
|
|
141
|
-
"strategy_status": {"message": "healthy"},
|
|
142
|
-
"gas_available": 0.0,
|
|
143
|
-
"gassed_up": True,
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
**Required methods**: `deposit`, `update`, `exit`, `_status`
|
|
148
|
-
|
|
149
|
-
**Optional methods**: `withdraw` (has default implementation), `partial_liquidate`, `setup`, `health_check`
|
|
150
|
-
|
|
151
|
-
### Adapters
|
|
152
|
-
|
|
153
|
-
Adapters wrap protocol-specific logic and expose capabilities to strategies:
|
|
154
|
-
|
|
155
|
-
```python
|
|
156
|
-
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
157
|
-
|
|
158
|
-
class MyAdapter(BaseAdapter):
|
|
159
|
-
adapter_type = "MY_ADAPTER"
|
|
160
|
-
|
|
161
|
-
def __init__(self, config=None):
|
|
162
|
-
super().__init__("my_adapter", config)
|
|
163
|
-
self.client = SomeClient()
|
|
164
|
-
|
|
165
|
-
async def connect(self) -> bool:
|
|
166
|
-
return True
|
|
167
|
-
|
|
168
|
-
async def do_something(self, param: str) -> tuple[bool, Any]:
|
|
169
|
-
try:
|
|
170
|
-
result = await self.client.call(param)
|
|
171
|
-
return (True, result)
|
|
172
|
-
except Exception as e:
|
|
173
|
-
return (False, str(e))
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
All adapter methods return `(success: bool, data: Any)` tuples.
|
|
177
|
-
|
|
178
|
-
### Built-in Adapters
|
|
179
|
-
|
|
180
|
-
| Adapter | Type | Purpose |
|
|
181
|
-
|---------|------|---------|
|
|
182
|
-
| BalanceAdapter | BALANCE | Wallet/token balances, cross-wallet transfers with ledger tracking |
|
|
183
|
-
| PoolAdapter | POOL | DeFi pool metadata and yield analytics |
|
|
184
|
-
| BRAPAdapter | BRAP | Cross-chain swap quotes and execution |
|
|
185
|
-
| LedgerAdapter | LEDGER | Transaction recording and net deposit tracking |
|
|
186
|
-
| TokenAdapter | TOKEN | Token metadata and price feeds |
|
|
187
|
-
| MoonwellAdapter | MOONWELL | Moonwell lending/borrowing on Base |
|
|
188
|
-
|
|
189
|
-
### Built-in Strategies
|
|
190
|
-
|
|
191
|
-
| Strategy | Description | Chain |
|
|
192
|
-
|----------|-------------|-------|
|
|
193
|
-
| stablecoin_yield_strategy | USDC yield optimization on Base | Base |
|
|
194
|
-
| hyperlend_stable_yield_strategy | Stablecoin yield on HyperLend | HyperEVM |
|
|
195
|
-
| moonwell_wsteth_loop_strategy | Leveraged wstETH carry trade | Base |
|
|
196
|
-
| basis_trading_strategy | Delta-neutral funding rate capture | Hyperliquid |
|
|
197
|
-
|
|
198
|
-
## Configuration
|
|
199
|
-
|
|
200
|
-
Configuration lives in `config.json`:
|
|
201
|
-
|
|
202
|
-
```json
|
|
203
|
-
{
|
|
204
|
-
"system": {
|
|
205
|
-
"api_base_url": "https://api.wayfinder.ai",
|
|
206
|
-
"api_key": "sk_live_..."
|
|
207
|
-
},
|
|
208
|
-
"strategy": {
|
|
209
|
-
"rpc_urls": {
|
|
210
|
-
"1": "https://eth.llamarpc.com",
|
|
211
|
-
"8453": "https://mainnet.base.org",
|
|
212
|
-
"42161": "https://arb1.arbitrum.io/rpc"
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
"wallets": [
|
|
216
|
-
{
|
|
217
|
-
"label": "main",
|
|
218
|
-
"address": "0x...",
|
|
219
|
-
"private_key_hex": "0x..."
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
"label": "stablecoin_yield_strategy",
|
|
223
|
-
"address": "0x...",
|
|
224
|
-
"private_key_hex": "0x..."
|
|
225
|
-
}
|
|
226
|
-
]
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
- **system.api_key**: Required for Wayfinder API authentication (sent as `X-API-KEY` header)
|
|
231
|
-
- **wallets**: Array of wallets with labels; strategies look up wallets by label matching their directory name
|
|
232
|
-
- **strategy.rpc_urls**: Custom RPC endpoints by chain ID
|
|
233
|
-
|
|
234
|
-
See [CONFIG_GUIDE.md](CONFIG_GUIDE.md) for detailed configuration documentation.
|
|
235
|
-
|
|
236
|
-
## CLI Usage
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
# Check strategy status
|
|
240
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action status --config config.json
|
|
241
|
-
|
|
242
|
-
# Deposit funds
|
|
243
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action deposit \
|
|
244
|
-
--main-token-amount 100 --gas-token-amount 0.01 --config config.json
|
|
245
|
-
|
|
246
|
-
# Run update cycle
|
|
247
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action update --config config.json
|
|
248
|
-
|
|
249
|
-
# Withdraw funds
|
|
250
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action withdraw --config config.json
|
|
251
|
-
|
|
252
|
-
# Exit (return funds to main wallet)
|
|
253
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action exit --config config.json
|
|
254
|
-
|
|
255
|
-
# Run continuously
|
|
256
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action run --config config.json
|
|
257
|
-
|
|
258
|
-
# Partial liquidation
|
|
259
|
-
poetry run python wayfinder_paths/run_strategy.py <strategy_name> --action partial-liquidate \
|
|
260
|
-
--amount 50 --config config.json
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**Available actions**: `status`, `deposit`, `update`, `withdraw`, `exit`, `run`, `partial-liquidate`, `policy`, `script`
|
|
264
|
-
|
|
265
|
-
## Testing
|
|
266
|
-
|
|
267
|
-
```bash
|
|
268
|
-
# Generate test wallets first
|
|
269
|
-
just create-wallets
|
|
270
|
-
|
|
271
|
-
# Run all smoke tests
|
|
272
|
-
poetry run pytest -k smoke -v
|
|
273
|
-
|
|
274
|
-
# Test specific strategy
|
|
275
|
-
poetry run pytest wayfinder_paths/strategies/my_strategy/ -v
|
|
276
|
-
|
|
277
|
-
# Test specific adapter
|
|
278
|
-
poetry run pytest wayfinder_paths/adapters/my_adapter/ -v
|
|
279
|
-
|
|
280
|
-
# Run with coverage
|
|
281
|
-
poetry run pytest --cov=wayfinder_paths -v
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
See [TESTING.md](TESTING.md) for detailed testing guidance.
|
|
285
|
-
|
|
286
|
-
## Contributing
|
|
287
|
-
|
|
288
|
-
### Creating a New Strategy
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
# Use the convenience command (creates wallet automatically)
|
|
292
|
-
just create-strategy "My Strategy Name"
|
|
293
|
-
|
|
294
|
-
# Or manually copy the template
|
|
295
|
-
cp -r wayfinder_paths/templates/strategy wayfinder_paths/strategies/my_strategy
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
Then:
|
|
299
|
-
1. Rename the class in `strategy.py`
|
|
300
|
-
2. Implement `deposit`, `update`, `exit`, and `_status` methods
|
|
301
|
-
3. Add tests in `test_strategy.py`
|
|
302
|
-
4. Create `examples.json` with test data
|
|
303
|
-
5. Update the README
|
|
304
|
-
|
|
305
|
-
### Creating a New Adapter
|
|
306
|
-
|
|
307
|
-
```bash
|
|
308
|
-
cp -r wayfinder_paths/templates/adapter wayfinder_paths/adapters/my_adapter
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Then:
|
|
312
|
-
1. Rename the class in `adapter.py`
|
|
313
|
-
2. Implement protocol-specific methods
|
|
314
|
-
3. Add tests in `test_adapter.py`
|
|
315
|
-
4. Update the README
|
|
316
|
-
|
|
317
|
-
### Guidelines
|
|
318
|
-
|
|
319
|
-
- Strategies call adapters, not clients directly
|
|
320
|
-
- All adapter methods return `(success, data)` tuples
|
|
321
|
-
- Use `examples.json` for strategy test data
|
|
322
|
-
- Never hardcode API keys or private keys
|
|
323
|
-
- Add tests before submitting PRs
|
|
324
|
-
|
|
325
|
-
## Publishing
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
# Must be on main branch
|
|
329
|
-
export PUBLISH_TOKEN="your_pypi_token"
|
|
330
|
-
just publish
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**Version bumping**: Update `version` in `pyproject.toml` before publishing. Follow [SemVer](https://semver.org/):
|
|
334
|
-
- PATCH: Bug fixes
|
|
335
|
-
- MINOR: New features (backward compatible)
|
|
336
|
-
- MAJOR: Breaking changes
|
|
337
|
-
|
|
338
|
-
## Security
|
|
339
|
-
|
|
340
|
-
- Never commit `config.json` (contains private keys)
|
|
341
|
-
- Use test wallets for development
|
|
342
|
-
- Test on testnets when available
|
|
343
|
-
- Validate all inputs
|
|
344
|
-
- Set appropriate gas limits
|
|
345
|
-
|
|
346
|
-
## Community
|
|
347
|
-
|
|
348
|
-
- [Discord](https://discord.gg/fUVwGMXjm3)
|
|
349
|
-
- [GitHub Issues](https://github.com/WayfinderFoundation/wayfinder-paths/issues)
|
|
350
|
-
|
|
351
|
-
## License
|
|
352
|
-
|
|
353
|
-
MIT License - see [LICENSE](LICENSE) for details.
|
|
354
|
-
|