wayfinder-paths 0.1.8__py3-none-any.whl → 0.1.10__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/CONFIG_GUIDE.md +6 -15
- wayfinder_paths/adapters/balance_adapter/README.md +1 -2
- wayfinder_paths/adapters/balance_adapter/adapter.py +4 -4
- wayfinder_paths/adapters/brap_adapter/README.md +1 -1
- wayfinder_paths/adapters/brap_adapter/adapter.py +139 -74
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -7
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +0 -54
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +1 -1
- wayfinder_paths/adapters/ledger_adapter/README.md +1 -1
- wayfinder_paths/adapters/moonwell_adapter/README.md +174 -0
- wayfinder_paths/adapters/moonwell_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +1226 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +635 -0
- wayfinder_paths/adapters/pool_adapter/README.md +1 -77
- wayfinder_paths/adapters/pool_adapter/adapter.py +0 -122
- wayfinder_paths/adapters/pool_adapter/examples.json +0 -57
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -86
- wayfinder_paths/adapters/token_adapter/README.md +1 -1
- wayfinder_paths/core/clients/ClientManager.py +1 -22
- wayfinder_paths/core/clients/WalletClient.py +0 -8
- wayfinder_paths/core/clients/WayfinderClient.py +7 -12
- wayfinder_paths/core/clients/__init__.py +0 -8
- wayfinder_paths/core/clients/protocols.py +0 -60
- wayfinder_paths/core/config.py +5 -45
- wayfinder_paths/core/constants/__init__.py +0 -2
- wayfinder_paths/core/constants/base.py +6 -2
- wayfinder_paths/core/constants/moonwell_abi.py +411 -0
- wayfinder_paths/core/services/base.py +7 -1
- wayfinder_paths/core/services/local_evm_txn.py +223 -222
- wayfinder_paths/core/services/local_token_txn.py +103 -92
- wayfinder_paths/core/services/web3_service.py +0 -2
- wayfinder_paths/core/settings.py +8 -8
- wayfinder_paths/core/strategies/Strategy.py +1 -5
- wayfinder_paths/core/strategies/descriptors.py +1 -1
- wayfinder_paths/core/utils/evm_helpers.py +7 -12
- wayfinder_paths/core/wallets/README.md +3 -6
- wayfinder_paths/run_strategy.py +62 -105
- wayfinder_paths/scripts/create_strategy.py +2 -27
- wayfinder_paths/scripts/make_wallets.py +1 -25
- wayfinder_paths/scripts/run_strategy.py +37 -9
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1 -3
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +87 -138
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +96 -58
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +2 -17
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +4 -1
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +107 -29
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +53 -14
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +108 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +11 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2975 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +886 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +2 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -4
- wayfinder_paths/templates/adapter/README.md +5 -21
- wayfinder_paths/templates/adapter/adapter.py +1 -2
- wayfinder_paths/templates/adapter/test_adapter.py +1 -1
- wayfinder_paths/templates/strategy/README.md +4 -21
- wayfinder_paths/templates/strategy/test_strategy.py +0 -4
- wayfinder_paths/tests/test_smoke_manifest.py +17 -2
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/METADATA +64 -201
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/RECORD +64 -71
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +0 -8
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +0 -11
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +0 -10
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +0 -8
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +0 -11
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +0 -10
- wayfinder_paths/adapters/token_adapter/manifest.yaml +0 -6
- wayfinder_paths/core/clients/SimulationClient.py +0 -192
- wayfinder_paths/core/clients/TransactionClient.py +0 -63
- wayfinder_paths/core/engine/manifest.py +0 -97
- wayfinder_paths/scripts/validate_manifests.py +0 -213
- wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +0 -23
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +0 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +0 -17
- wayfinder_paths/templates/adapter/manifest.yaml +0 -6
- wayfinder_paths/templates/strategy/manifest.yaml +0 -8
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/WHEEL +0 -0
wayfinder_paths/run_strategy.py
CHANGED
|
@@ -12,8 +12,7 @@ from pathlib import Path
|
|
|
12
12
|
|
|
13
13
|
from loguru import logger
|
|
14
14
|
|
|
15
|
-
from wayfinder_paths.core.config import StrategyJobConfig
|
|
16
|
-
from wayfinder_paths.core.engine.manifest import load_manifest, validate_manifest
|
|
15
|
+
from wayfinder_paths.core.config import StrategyJobConfig
|
|
17
16
|
from wayfinder_paths.core.engine.StrategyJob import StrategyJob
|
|
18
17
|
|
|
19
18
|
|
|
@@ -21,82 +20,101 @@ def load_strategy(
|
|
|
21
20
|
strategy_name: str,
|
|
22
21
|
*,
|
|
23
22
|
strategy_config: dict | None = None,
|
|
24
|
-
simulation: bool = False,
|
|
25
23
|
api_key: str | None = None,
|
|
26
24
|
):
|
|
27
25
|
"""
|
|
28
|
-
Dynamically load a strategy by name
|
|
26
|
+
Dynamically load a strategy by name
|
|
29
27
|
|
|
30
28
|
Args:
|
|
31
29
|
strategy_name: Name of the strategy to load (directory name in strategies/)
|
|
32
30
|
strategy_config: Configuration dict for the strategy
|
|
33
|
-
simulation: Enable simulation mode for testing
|
|
34
31
|
api_key: Optional API key for service account authentication
|
|
35
32
|
|
|
36
33
|
Returns:
|
|
37
34
|
Strategy instance
|
|
38
35
|
"""
|
|
39
|
-
#
|
|
36
|
+
# Build the expected module path from strategy name
|
|
40
37
|
strategies_dir = Path(__file__).parent / "strategies"
|
|
41
38
|
strategy_dir = strategies_dir / strategy_name
|
|
42
|
-
manifest_path = strategy_dir / "manifest.yaml"
|
|
43
39
|
|
|
44
|
-
if not
|
|
40
|
+
if not strategy_dir.exists():
|
|
45
41
|
# List available strategies for better error message
|
|
46
42
|
available = []
|
|
47
43
|
if strategies_dir.exists():
|
|
48
44
|
for path in strategies_dir.iterdir():
|
|
49
|
-
if path.is_dir() and (path / "
|
|
45
|
+
if path.is_dir() and (path / "strategy.py").exists():
|
|
50
46
|
available.append(path.name)
|
|
51
47
|
available_str = ", ".join(available) if available else "none"
|
|
52
48
|
raise ValueError(
|
|
53
49
|
f"Unknown strategy: {strategy_name}. Available strategies: {available_str}"
|
|
54
50
|
)
|
|
55
51
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
module_path,
|
|
59
|
-
module = __import__(module_path, fromlist=[class_name])
|
|
60
|
-
strategy_class = getattr(module, class_name)
|
|
52
|
+
# Import strategy module and find Strategy class
|
|
53
|
+
module_path = f"strategies.{strategy_name}.strategy"
|
|
54
|
+
module = __import__(module_path, fromlist=[""])
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
# Find the Strategy subclass in the module
|
|
57
|
+
from wayfinder_paths.core.strategies.Strategy import Strategy
|
|
58
|
+
|
|
59
|
+
strategy_class = None
|
|
60
|
+
for attr_name in dir(module):
|
|
61
|
+
attr = getattr(module, attr_name)
|
|
62
|
+
if (
|
|
63
|
+
isinstance(attr, type)
|
|
64
|
+
and issubclass(attr, Strategy)
|
|
65
|
+
and attr is not Strategy
|
|
66
|
+
):
|
|
67
|
+
strategy_class = attr
|
|
68
|
+
break
|
|
69
|
+
|
|
70
|
+
if strategy_class is None:
|
|
71
|
+
raise ValueError(f"No Strategy class found in {module_path}")
|
|
72
|
+
|
|
73
|
+
return strategy_class(config=strategy_config, api_key=api_key)
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
def load_config(
|
|
68
77
|
config_path: str | None = None, strategy_name: str | None = None
|
|
69
78
|
) -> StrategyJobConfig:
|
|
70
79
|
"""
|
|
71
|
-
Load configuration from file
|
|
80
|
+
Load configuration from config.json file
|
|
72
81
|
|
|
73
82
|
Args:
|
|
74
|
-
config_path:
|
|
83
|
+
config_path: Path to config file (defaults to "config.json")
|
|
75
84
|
strategy_name: Optional strategy name for per-strategy wallet lookup
|
|
76
85
|
|
|
77
86
|
Returns:
|
|
78
87
|
StrategyJobConfig instance
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
FileNotFoundError: If config file does not exist
|
|
79
91
|
"""
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
# Default to config.json if not provided
|
|
93
|
+
if not config_path:
|
|
94
|
+
config_path = "config.json"
|
|
95
|
+
|
|
96
|
+
config_file = Path(config_path)
|
|
97
|
+
if not config_file.exists():
|
|
98
|
+
raise FileNotFoundError(
|
|
99
|
+
f"Config file not found: {config_path}. "
|
|
100
|
+
"Please create config.json (see wayfinder_paths/config.example.json for template)"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
logger.info(f"Loading config from {config_path}")
|
|
104
|
+
with open(config_file) as f:
|
|
105
|
+
config_data = json.load(f)
|
|
106
|
+
|
|
107
|
+
config = StrategyJobConfig.from_dict(config_data, strategy_name=strategy_name)
|
|
108
|
+
if strategy_name:
|
|
109
|
+
config.strategy_config["_strategy_name"] = strategy_name
|
|
110
|
+
config.__post_init__()
|
|
111
|
+
return config
|
|
92
112
|
|
|
93
113
|
|
|
94
114
|
async def run_strategy(
|
|
95
115
|
strategy_name: str | None = None,
|
|
96
116
|
config_path: str | None = None,
|
|
97
117
|
action: str = "run",
|
|
98
|
-
manifest_path: str | None = None,
|
|
99
|
-
simulation: bool = False,
|
|
100
118
|
**kwargs,
|
|
101
119
|
):
|
|
102
120
|
"""
|
|
@@ -109,37 +127,14 @@ async def run_strategy(
|
|
|
109
127
|
**kwargs: Additional arguments for the action
|
|
110
128
|
"""
|
|
111
129
|
try:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if manifest_path:
|
|
117
|
-
logger.debug(f"Loading strategy via manifest: {manifest_path}")
|
|
118
|
-
manifest = load_manifest(manifest_path)
|
|
119
|
-
validate_manifest(manifest)
|
|
120
|
-
# Extract directory name from manifest path for wallet lookup
|
|
121
|
-
# Use the directory name (strategy identifier) for wallet lookup
|
|
122
|
-
manifest_dir = Path(manifest_path).parent
|
|
123
|
-
strategies_dir = Path(__file__).parent / "strategies"
|
|
124
|
-
try:
|
|
125
|
-
# Try to get relative path - if it's under strategies_dir, use directory name
|
|
126
|
-
rel_path = manifest_dir.relative_to(strategies_dir)
|
|
127
|
-
strategy_name_for_wallet = (
|
|
128
|
-
rel_path.parts[0] if rel_path.parts else manifest_dir.name
|
|
129
|
-
)
|
|
130
|
-
except ValueError:
|
|
131
|
-
# Not under strategies_dir, fallback to directory name or manifest name
|
|
132
|
-
strategy_name_for_wallet = manifest_dir.name or manifest.name
|
|
133
|
-
else:
|
|
134
|
-
if not strategy_name:
|
|
135
|
-
raise ValueError("Either strategy_name or --manifest must be provided")
|
|
136
|
-
logger.debug(f"Loading strategy by name: {strategy_name}")
|
|
137
|
-
# Use directory name (strategy_name) directly for wallet lookup
|
|
138
|
-
strategy_name_for_wallet = strategy_name
|
|
130
|
+
if not strategy_name:
|
|
131
|
+
raise ValueError("strategy_name is required")
|
|
132
|
+
|
|
133
|
+
logger.debug(f"Loading strategy by name: {strategy_name}")
|
|
139
134
|
|
|
140
135
|
# Load configuration with strategy name for wallet lookup
|
|
141
136
|
logger.debug(f"Config path provided: {config_path}")
|
|
142
|
-
config = load_config(config_path, strategy_name=
|
|
137
|
+
config = load_config(config_path, strategy_name=strategy_name)
|
|
143
138
|
logger.debug(
|
|
144
139
|
"Loaded config: creds=%s wallets(main=%s strategy=%s)",
|
|
145
140
|
"yes"
|
|
@@ -150,28 +145,12 @@ async def run_strategy(
|
|
|
150
145
|
(config.user.strategy_wallet_address or "none"),
|
|
151
146
|
)
|
|
152
147
|
|
|
153
|
-
# Validate required configuration
|
|
154
|
-
# No user id required; authentication is via credentials or refresh token
|
|
155
|
-
|
|
156
148
|
# Load strategy with the enriched config
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
strategy = strategy_class(
|
|
163
|
-
config=config.strategy_config, simulation=simulation
|
|
164
|
-
)
|
|
165
|
-
logger.info(
|
|
166
|
-
f"Loaded strategy from manifest: {strategy_name_for_wallet or 'unnamed'}"
|
|
167
|
-
)
|
|
168
|
-
else:
|
|
169
|
-
strategy = load_strategy(
|
|
170
|
-
strategy_name,
|
|
171
|
-
strategy_config=config.strategy_config,
|
|
172
|
-
simulation=simulation,
|
|
173
|
-
)
|
|
174
|
-
logger.info(f"Loaded strategy: {strategy.name}")
|
|
149
|
+
strategy = load_strategy(
|
|
150
|
+
strategy_name,
|
|
151
|
+
strategy_config=config.strategy_config,
|
|
152
|
+
)
|
|
153
|
+
logger.info(f"Loaded strategy: {strategy.name}")
|
|
175
154
|
|
|
176
155
|
# Create strategy job
|
|
177
156
|
strategy_job = StrategyJob(strategy, config)
|
|
@@ -248,16 +227,6 @@ async def run_strategy(
|
|
|
248
227
|
except Exception:
|
|
249
228
|
pass
|
|
250
229
|
|
|
251
|
-
if not policies and manifest and getattr(manifest, "permissions", None):
|
|
252
|
-
try:
|
|
253
|
-
mpol = manifest.permissions.get("policy")
|
|
254
|
-
if isinstance(mpol, str):
|
|
255
|
-
policies = [mpol]
|
|
256
|
-
elif isinstance(mpol, list):
|
|
257
|
-
policies = [p for p in mpol if isinstance(p, str)]
|
|
258
|
-
except Exception:
|
|
259
|
-
pass
|
|
260
|
-
|
|
261
230
|
seen = set()
|
|
262
231
|
deduped: list[str] = []
|
|
263
232
|
for p in policies:
|
|
@@ -318,15 +287,10 @@ def main():
|
|
|
318
287
|
parser = argparse.ArgumentParser(description="Run strategy strategies")
|
|
319
288
|
parser.add_argument(
|
|
320
289
|
"strategy",
|
|
321
|
-
nargs="?",
|
|
322
290
|
help="Strategy to run (stablecoin_yield_strategy)",
|
|
323
291
|
)
|
|
324
292
|
parser.add_argument(
|
|
325
|
-
"--
|
|
326
|
-
help="Path to strategy manifest YAML (alternative to strategy name)",
|
|
327
|
-
)
|
|
328
|
-
parser.add_argument(
|
|
329
|
-
"--config", help="Path to config file (defaults to environment variables)"
|
|
293
|
+
"--config", help="Path to config file (defaults to config.json)"
|
|
330
294
|
)
|
|
331
295
|
parser.add_argument(
|
|
332
296
|
"--action",
|
|
@@ -372,11 +336,6 @@ def main():
|
|
|
372
336
|
"--duration", type=int, help="Duration in seconds for script action"
|
|
373
337
|
)
|
|
374
338
|
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
|
375
|
-
parser.add_argument(
|
|
376
|
-
"--simulation",
|
|
377
|
-
action="store_true",
|
|
378
|
-
help="Run in simulation mode (no real transactions)",
|
|
379
|
-
)
|
|
380
339
|
parser.add_argument(
|
|
381
340
|
"--wallet-id",
|
|
382
341
|
help="Wallet ID for policy rendering (replaces FORMAT_WALLET_ID in policies)",
|
|
@@ -395,13 +354,11 @@ def main():
|
|
|
395
354
|
strategy_name=args.strategy,
|
|
396
355
|
config_path=args.config,
|
|
397
356
|
action=args.action,
|
|
398
|
-
manifest_path=args.manifest,
|
|
399
357
|
amount=args.amount,
|
|
400
358
|
main_token_amount=args.main_token_amount,
|
|
401
359
|
gas_token_amount=args.gas_token_amount,
|
|
402
360
|
interval=args.interval,
|
|
403
361
|
duration=args.duration,
|
|
404
|
-
simulation=args.simulation,
|
|
405
362
|
wallet_id=getattr(args, "wallet_id", None),
|
|
406
363
|
)
|
|
407
364
|
)
|
|
@@ -5,7 +5,7 @@ Create a new strategy from template and generate a dedicated wallet for it.
|
|
|
5
5
|
This script:
|
|
6
6
|
1. Copies the strategy template to a new directory
|
|
7
7
|
2. Generates a wallet with label matching the strategy name
|
|
8
|
-
3. Updates the
|
|
8
|
+
3. Updates the strategy files with the correct names
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import argparse
|
|
@@ -13,8 +13,6 @@ import re
|
|
|
13
13
|
import shutil
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
-
import yaml
|
|
17
|
-
|
|
18
16
|
from wayfinder_paths.core.utils.wallets import make_random_wallet, write_wallet_to_json
|
|
19
17
|
|
|
20
18
|
|
|
@@ -29,18 +27,6 @@ def sanitize_name(name: str) -> str:
|
|
|
29
27
|
return name.lower()
|
|
30
28
|
|
|
31
29
|
|
|
32
|
-
def update_manifest(manifest_path: Path, strategy_name: str, entrypoint: str) -> None:
|
|
33
|
-
"""Update manifest.yaml with strategy name and entrypoint."""
|
|
34
|
-
with open(manifest_path) as f:
|
|
35
|
-
manifest_data = yaml.safe_load(f)
|
|
36
|
-
|
|
37
|
-
manifest_data["name"] = strategy_name
|
|
38
|
-
manifest_data["entrypoint"] = entrypoint
|
|
39
|
-
|
|
40
|
-
with open(manifest_path, "w") as f:
|
|
41
|
-
yaml.dump(manifest_data, f, default_flow_style=False, sort_keys=False)
|
|
42
|
-
|
|
43
|
-
|
|
44
30
|
def update_strategy_file(strategy_path: Path, class_name: str) -> None:
|
|
45
31
|
"""Update strategy.py with new class name."""
|
|
46
32
|
content = strategy_path.read_text()
|
|
@@ -111,7 +97,6 @@ def main():
|
|
|
111
97
|
# Copy template files
|
|
112
98
|
template_files = [
|
|
113
99
|
"strategy.py",
|
|
114
|
-
"manifest.yaml",
|
|
115
100
|
"test_strategy.py",
|
|
116
101
|
"examples.json",
|
|
117
102
|
"README.md",
|
|
@@ -135,15 +120,6 @@ def main():
|
|
|
135
120
|
update_strategy_file(strategy_file, class_name)
|
|
136
121
|
print(f" Updated strategy.py with class name: {class_name}")
|
|
137
122
|
|
|
138
|
-
# Generate entrypoint path
|
|
139
|
-
entrypoint = f"strategies.{dir_name}.strategy.{class_name}"
|
|
140
|
-
|
|
141
|
-
# Update manifest with name (using directory name) and entrypoint
|
|
142
|
-
manifest_path = strategy_dir / "manifest.yaml"
|
|
143
|
-
if manifest_path.exists():
|
|
144
|
-
update_manifest(manifest_path, dir_name, entrypoint)
|
|
145
|
-
print(f" Updated manifest.yaml with name: {dir_name}")
|
|
146
|
-
|
|
147
123
|
# Generate wallet with label matching directory name (strategy identifier)
|
|
148
124
|
# If wallets.json doesn't exist, create it with a main wallet first
|
|
149
125
|
if not args.wallets_file.exists():
|
|
@@ -169,11 +145,10 @@ def main():
|
|
|
169
145
|
print(f" Directory: {strategy_dir}")
|
|
170
146
|
print(f" Name: {dir_name}")
|
|
171
147
|
print(f" Class: {class_name}")
|
|
172
|
-
print(f" Entrypoint: {entrypoint}")
|
|
173
148
|
print(f" Wallet: {wallet['address']}")
|
|
174
149
|
print("\nNext steps:")
|
|
175
150
|
print(f" 1. Edit {strategy_dir / 'strategy.py'} to implement your strategy")
|
|
176
|
-
print(
|
|
151
|
+
print(" 2. Add required adapters in __init__")
|
|
177
152
|
print(f" 3. Test with: just test-strategy {dir_name}")
|
|
178
153
|
|
|
179
154
|
|
|
@@ -15,27 +15,6 @@ def to_keystore_json(private_key_hex: str, password: str):
|
|
|
15
15
|
return Account.encrypt(private_key_hex, password)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def write_env(rows: list[dict[str, str]], out_dir: Path) -> None:
|
|
19
|
-
with open(out_dir / ".env.example", "w") as f:
|
|
20
|
-
if rows:
|
|
21
|
-
label_to_wallet = {r.get("label"): r for r in rows if r.get("label")}
|
|
22
|
-
main_w = (
|
|
23
|
-
label_to_wallet.get("main") or label_to_wallet.get("default") or rows[0]
|
|
24
|
-
)
|
|
25
|
-
strategy_w = label_to_wallet.get("strategy")
|
|
26
|
-
|
|
27
|
-
f.write("RPC_URL=https://rpc.ankr.com/eth\n")
|
|
28
|
-
# Back-compat defaults
|
|
29
|
-
f.write(f"PRIVATE_KEY={main_w['private_key_hex']}\n")
|
|
30
|
-
f.write(f"FROM_ADDRESS={main_w['address']}\n")
|
|
31
|
-
# Explicit main/strategy variables
|
|
32
|
-
f.write(f"MAIN_WALLET_ADDRESS={main_w['address']}\n")
|
|
33
|
-
if strategy_w:
|
|
34
|
-
f.write(f"STRATEGY_WALLET_ADDRESS={strategy_w['address']}\n")
|
|
35
|
-
# Optional: expose strategy private key for local dev only
|
|
36
|
-
f.write(f"PRIVATE_KEY_STRATEGY={strategy_w['private_key_hex']}\n")
|
|
37
|
-
|
|
38
|
-
|
|
39
18
|
def main():
|
|
40
19
|
parser = argparse.ArgumentParser(description="Generate local dev wallets")
|
|
41
20
|
parser.add_argument(
|
|
@@ -48,7 +27,7 @@ def main():
|
|
|
48
27
|
"--out-dir",
|
|
49
28
|
type=Path,
|
|
50
29
|
default=Path("."),
|
|
51
|
-
help="Output directory for wallets.json (and
|
|
30
|
+
help="Output directory for wallets.json (and keystore files)",
|
|
52
31
|
)
|
|
53
32
|
parser.add_argument(
|
|
54
33
|
"--keystore-password",
|
|
@@ -161,9 +140,6 @@ def main():
|
|
|
161
140
|
ks_path.write_text(json.dumps(ks))
|
|
162
141
|
index += 1
|
|
163
142
|
|
|
164
|
-
# Convenience outputs
|
|
165
|
-
write_env(rows, args.out_dir)
|
|
166
|
-
|
|
167
143
|
|
|
168
144
|
if __name__ == "__main__":
|
|
169
145
|
main()
|
|
@@ -14,6 +14,15 @@ def _load_wallets(path: Path) -> list[dict[str, Any]]:
|
|
|
14
14
|
return [w for w in data if isinstance(w, dict)]
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _load_config(path: Path) -> dict[str, Any]:
|
|
18
|
+
if not path.exists():
|
|
19
|
+
return {}
|
|
20
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
21
|
+
if not isinstance(data, dict):
|
|
22
|
+
raise ValueError(f"Expected a dict in {path}")
|
|
23
|
+
return data
|
|
24
|
+
|
|
25
|
+
|
|
17
26
|
def _find_wallet(wallets: list[dict[str, Any]], label: str) -> dict[str, Any]:
|
|
18
27
|
for w in wallets:
|
|
19
28
|
if w.get("label") == label:
|
|
@@ -36,6 +45,13 @@ def _get_strategy_class(strategy: str):
|
|
|
36
45
|
|
|
37
46
|
return HyperlendStableYieldStrategy
|
|
38
47
|
|
|
48
|
+
if strategy == "moonwell_wsteth_loop_strategy":
|
|
49
|
+
from wayfinder_paths.strategies.moonwell_wsteth_loop_strategy.strategy import (
|
|
50
|
+
MoonwellWstethLoopStrategy,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return MoonwellWstethLoopStrategy
|
|
54
|
+
|
|
39
55
|
raise ValueError(f"Unknown strategy: {strategy}")
|
|
40
56
|
|
|
41
57
|
|
|
@@ -44,19 +60,25 @@ async def _run(args: argparse.Namespace) -> int:
|
|
|
44
60
|
wallets_path = (
|
|
45
61
|
Path(args.wallets).resolve() if args.wallets else repo_root / "wallets.json"
|
|
46
62
|
)
|
|
63
|
+
config_path = (
|
|
64
|
+
Path(args.config).resolve() if args.config else repo_root / "config.json"
|
|
65
|
+
)
|
|
66
|
+
|
|
47
67
|
wallets = _load_wallets(wallets_path)
|
|
68
|
+
config = _load_config(config_path)
|
|
48
69
|
|
|
49
70
|
main_wallet = _find_wallet(wallets, args.main_wallet_label)
|
|
50
71
|
strategy_wallet = _find_wallet(wallets, args.strategy_wallet_label)
|
|
51
72
|
|
|
73
|
+
# Merge config with wallet info
|
|
74
|
+
strategy_config = {
|
|
75
|
+
"main_wallet": main_wallet,
|
|
76
|
+
"strategy_wallet": strategy_wallet,
|
|
77
|
+
**config.get("strategy", {}),
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
strategy_class = _get_strategy_class(args.strategy)
|
|
53
|
-
s = strategy_class(
|
|
54
|
-
{
|
|
55
|
-
"main_wallet": main_wallet,
|
|
56
|
-
"strategy_wallet": strategy_wallet,
|
|
57
|
-
},
|
|
58
|
-
simulation=args.simulation,
|
|
59
|
-
)
|
|
81
|
+
s = strategy_class(strategy_config)
|
|
60
82
|
|
|
61
83
|
await s.setup()
|
|
62
84
|
|
|
@@ -94,14 +116,20 @@ def main() -> int:
|
|
|
94
116
|
p.add_argument(
|
|
95
117
|
"--strategy",
|
|
96
118
|
default="basis_trading_strategy",
|
|
97
|
-
choices=[
|
|
119
|
+
choices=[
|
|
120
|
+
"basis_trading_strategy",
|
|
121
|
+
"hyperlend_stable_yield_strategy",
|
|
122
|
+
"moonwell_wsteth_loop_strategy",
|
|
123
|
+
],
|
|
98
124
|
)
|
|
99
125
|
p.add_argument(
|
|
100
126
|
"--wallets", default=None, help="Path to wallets.json (default: repo root)"
|
|
101
127
|
)
|
|
128
|
+
p.add_argument(
|
|
129
|
+
"--config", default=None, help="Path to config.json (default: repo root)"
|
|
130
|
+
)
|
|
102
131
|
p.add_argument("--main-wallet-label", default="main")
|
|
103
132
|
p.add_argument("--strategy-wallet-label", default="basis_trading_strategy")
|
|
104
|
-
p.add_argument("--simulation", action="store_true")
|
|
105
133
|
|
|
106
134
|
sub = p.add_subparsers(dest="command", required=True)
|
|
107
135
|
|
|
@@ -8,7 +8,6 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import json
|
|
11
|
-
import os
|
|
12
11
|
import time
|
|
13
12
|
from datetime import UTC, datetime
|
|
14
13
|
from decimal import ROUND_DOWN, Decimal
|
|
@@ -1007,5 +1006,4 @@ class BasisSnapshotMixin:
|
|
|
1007
1006
|
)
|
|
1008
1007
|
if isinstance(val, str) and val.strip():
|
|
1009
1008
|
return val.strip()
|
|
1010
|
-
|
|
1011
|
-
return env.strip() if isinstance(env, str) and env.strip() else None
|
|
1009
|
+
return None
|