wayfinder-paths 0.1.11__py3-none-any.whl → 0.1.14__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/balance_adapter/README.md +13 -14
- wayfinder_paths/adapters/balance_adapter/adapter.py +36 -39
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
- wayfinder_paths/adapters/brap_adapter/README.md +11 -16
- wayfinder_paths/adapters/brap_adapter/adapter.py +87 -75
- wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +22 -23
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +1 -1
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +44 -5
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +104 -0
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +0 -3
- wayfinder_paths/adapters/pool_adapter/README.md +11 -27
- wayfinder_paths/adapters/pool_adapter/adapter.py +11 -37
- wayfinder_paths/adapters/pool_adapter/examples.json +6 -7
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +8 -8
- wayfinder_paths/adapters/token_adapter/README.md +2 -14
- wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
- wayfinder_paths/adapters/token_adapter/examples.json +4 -8
- wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
- wayfinder_paths/core/clients/BRAPClient.py +103 -62
- wayfinder_paths/core/clients/ClientManager.py +1 -68
- wayfinder_paths/core/clients/HyperlendClient.py +127 -66
- wayfinder_paths/core/clients/LedgerClient.py +1 -4
- wayfinder_paths/core/clients/PoolClient.py +126 -88
- wayfinder_paths/core/clients/TokenClient.py +92 -37
- wayfinder_paths/core/clients/WalletClient.py +28 -58
- wayfinder_paths/core/clients/WayfinderClient.py +33 -166
- wayfinder_paths/core/clients/__init__.py +0 -2
- wayfinder_paths/core/clients/protocols.py +35 -52
- wayfinder_paths/core/clients/sdk_example.py +37 -22
- wayfinder_paths/core/config.py +60 -224
- wayfinder_paths/core/engine/StrategyJob.py +7 -55
- wayfinder_paths/core/services/local_evm_txn.py +28 -10
- wayfinder_paths/core/services/local_token_txn.py +1 -1
- wayfinder_paths/core/strategies/Strategy.py +3 -5
- wayfinder_paths/core/strategies/descriptors.py +7 -0
- wayfinder_paths/core/utils/evm_helpers.py +7 -3
- wayfinder_paths/core/utils/wallets.py +12 -19
- wayfinder_paths/core/wallets/README.md +1 -1
- wayfinder_paths/run_strategy.py +8 -17
- wayfinder_paths/scripts/create_strategy.py +5 -5
- wayfinder_paths/scripts/make_wallets.py +5 -5
- wayfinder_paths/scripts/run_strategy.py +3 -3
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1 -1
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +206 -526
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +228 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +2 -2
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +41 -25
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +1 -1
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +10 -9
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +3 -3
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +110 -78
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +44 -21
- wayfinder_paths/templates/adapter/README.md +1 -1
- wayfinder_paths/templates/strategy/README.md +3 -3
- wayfinder_paths/templates/strategy/test_strategy.py +3 -2
- {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +21 -59
- {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +64 -65
- wayfinder_paths/core/clients/AuthClient.py +0 -83
- wayfinder_paths/core/settings.py +0 -61
- {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/WHEEL +0 -0
|
@@ -43,12 +43,12 @@ def resolve_chain_id(token_info: dict[str, Any], logger_instance=None) -> int |
|
|
|
43
43
|
"""
|
|
44
44
|
log = logger_instance or logger
|
|
45
45
|
chain_meta = token_info.get("chain") or {}
|
|
46
|
-
chain_id = chain_meta.get("
|
|
46
|
+
chain_id = chain_meta.get("id")
|
|
47
47
|
try:
|
|
48
48
|
if chain_id is not None:
|
|
49
49
|
return int(chain_id)
|
|
50
50
|
except (ValueError, TypeError):
|
|
51
|
-
log.debug("Invalid chain_id in token_info.
|
|
51
|
+
log.debug("Invalid chain_id in token_info.chain: %s", chain_id)
|
|
52
52
|
return chain_code_to_chain_id(chain_meta.get("code"))
|
|
53
53
|
|
|
54
54
|
|
|
@@ -78,9 +78,13 @@ def resolve_rpc_url(
|
|
|
78
78
|
if chain_id is not None and isinstance(mapping, dict):
|
|
79
79
|
by_int = mapping.get(chain_id)
|
|
80
80
|
if by_int:
|
|
81
|
+
if isinstance(by_int, list):
|
|
82
|
+
return str(by_int[0])
|
|
81
83
|
return str(by_int)
|
|
82
84
|
by_str = mapping.get(str(chain_id))
|
|
83
85
|
if by_str:
|
|
86
|
+
if isinstance(by_str, list):
|
|
87
|
+
return str(by_str[0])
|
|
84
88
|
return str(by_str)
|
|
85
89
|
raise ValueError("RPC URL not provided. Set strategy.rpc_urls in config.json.")
|
|
86
90
|
|
|
@@ -133,7 +137,7 @@ def resolve_private_key_for_from_address(
|
|
|
133
137
|
if strategy_addr and from_addr_norm == (strategy_addr or "").lower():
|
|
134
138
|
return strategy_pk
|
|
135
139
|
|
|
136
|
-
# No fallback - private keys must be in config or
|
|
140
|
+
# No fallback - private keys must be in config or config.json
|
|
137
141
|
return None
|
|
138
142
|
|
|
139
143
|
|
|
@@ -6,11 +6,7 @@ from eth_account import Account
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def make_random_wallet() -> dict[str, str]:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Returns a mapping with keys: "address" and "private_key_hex" (0x-prefixed).
|
|
12
|
-
"""
|
|
13
|
-
acct = Account.create() # uses os.urandom
|
|
9
|
+
acct = Account.create()
|
|
14
10
|
return {
|
|
15
11
|
"address": acct.address,
|
|
16
12
|
"private_key_hex": acct.key.hex(),
|
|
@@ -22,33 +18,31 @@ def _load_existing_wallets(file_path: Path) -> list[dict[str, Any]]:
|
|
|
22
18
|
return []
|
|
23
19
|
try:
|
|
24
20
|
parsed = json.loads(file_path.read_text())
|
|
25
|
-
if isinstance(parsed, list):
|
|
26
|
-
return parsed
|
|
27
21
|
if isinstance(parsed, dict):
|
|
28
22
|
wallets = parsed.get("wallets")
|
|
29
23
|
if isinstance(wallets, list):
|
|
30
24
|
return wallets
|
|
31
25
|
return []
|
|
32
26
|
except Exception:
|
|
33
|
-
# If the file is malformed, start fresh rather than raising.
|
|
34
27
|
return []
|
|
35
28
|
|
|
36
29
|
|
|
37
30
|
def _save_wallets(file_path: Path, wallets: list[dict[str, Any]]) -> None:
|
|
38
|
-
|
|
31
|
+
config = {}
|
|
32
|
+
if file_path.exists():
|
|
33
|
+
try:
|
|
34
|
+
config = json.loads(file_path.read_text())
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
|
|
39
38
|
sorted_wallets = sorted(wallets, key=lambda w: w.get("address", ""))
|
|
40
|
-
|
|
39
|
+
config["wallets"] = sorted_wallets
|
|
40
|
+
file_path.write_text(json.dumps(config, indent=2))
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def write_wallet_to_json(
|
|
44
|
-
wallet: dict[str, str], out_dir: str | Path = ".", filename: str = "
|
|
44
|
+
wallet: dict[str, str], out_dir: str | Path = ".", filename: str = "config.json"
|
|
45
45
|
) -> Path:
|
|
46
|
-
"""Create or update a wallets.json with the provided wallet.
|
|
47
|
-
|
|
48
|
-
- Ensures the output directory exists.
|
|
49
|
-
- Merges with existing entries keyed by address (updates if present, appends otherwise).
|
|
50
|
-
- Writes a pretty-printed JSON list of wallet objects.
|
|
51
|
-
"""
|
|
52
46
|
out_dir_path = Path(out_dir)
|
|
53
47
|
out_dir_path.mkdir(parents=True, exist_ok=True)
|
|
54
48
|
file_path = out_dir_path / filename
|
|
@@ -71,7 +65,6 @@ def write_wallet_to_json(
|
|
|
71
65
|
|
|
72
66
|
|
|
73
67
|
def load_wallets(
|
|
74
|
-
out_dir: str | Path = ".", filename: str = "
|
|
68
|
+
out_dir: str | Path = ".", filename: str = "config.json"
|
|
75
69
|
) -> list[dict[str, Any]]:
|
|
76
|
-
"""Public helper to read wallets.json as a list of wallet dicts."""
|
|
77
70
|
return _load_existing_wallets(Path(out_dir) / filename)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wallet Abstraction Layer
|
|
2
2
|
|
|
3
|
-
Wayfinder strategies interact with blockchains through a single abstraction: the `EvmTxn` interface defined in `wayfinder_paths/core/services/base.py`. The default implementation (`LocalEvmTxn`) signs transactions with private keys pulled from config.json or
|
|
3
|
+
Wayfinder strategies interact with blockchains through a single abstraction: the `EvmTxn` interface defined in `wayfinder_paths/core/services/base.py`. The default implementation (`LocalEvmTxn`) signs transactions with private keys pulled from config.json or config.json, while `WalletManager` resolves which provider to use at runtime.
|
|
4
4
|
|
|
5
5
|
## Pieces
|
|
6
6
|
|
wayfinder_paths/run_strategy.py
CHANGED
|
@@ -20,7 +20,6 @@ def load_strategy(
|
|
|
20
20
|
strategy_name: str,
|
|
21
21
|
*,
|
|
22
22
|
strategy_config: dict | None = None,
|
|
23
|
-
api_key: str | None = None,
|
|
24
23
|
):
|
|
25
24
|
"""
|
|
26
25
|
Dynamically load a strategy by name
|
|
@@ -28,7 +27,6 @@ def load_strategy(
|
|
|
28
27
|
Args:
|
|
29
28
|
strategy_name: Name of the strategy to load (directory name in strategies/)
|
|
30
29
|
strategy_config: Configuration dict for the strategy
|
|
31
|
-
api_key: Optional API key for service account authentication
|
|
32
30
|
|
|
33
31
|
Returns:
|
|
34
32
|
Strategy instance
|
|
@@ -70,7 +68,7 @@ def load_strategy(
|
|
|
70
68
|
if strategy_class is None:
|
|
71
69
|
raise ValueError(f"No Strategy class found in {module_path}")
|
|
72
70
|
|
|
73
|
-
return strategy_class(config=strategy_config
|
|
71
|
+
return strategy_class(config=strategy_config)
|
|
74
72
|
|
|
75
73
|
|
|
76
74
|
def load_config(
|
|
@@ -136,15 +134,14 @@ async def run_strategy(
|
|
|
136
134
|
logger.debug(f"Config path provided: {config_path}")
|
|
137
135
|
config = load_config(config_path, strategy_name=strategy_name)
|
|
138
136
|
logger.debug(
|
|
139
|
-
"Loaded config:
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
or config.user.refresh_token
|
|
143
|
-
else "no",
|
|
144
|
-
(config.user.main_wallet_address or "none"),
|
|
145
|
-
(config.user.strategy_wallet_address or "none"),
|
|
137
|
+
"Loaded config: wallets(main={} strategy={})",
|
|
138
|
+
config.user.main_wallet_address or "none",
|
|
139
|
+
config.user.strategy_wallet_address or "none",
|
|
146
140
|
)
|
|
147
141
|
|
|
142
|
+
# Validate required configuration
|
|
143
|
+
# Authentication is via system.api_key in config.json
|
|
144
|
+
|
|
148
145
|
# Load strategy with the enriched config
|
|
149
146
|
strategy = load_strategy(
|
|
150
147
|
strategy_name,
|
|
@@ -157,13 +154,7 @@ async def run_strategy(
|
|
|
157
154
|
|
|
158
155
|
# Setup strategy job
|
|
159
156
|
logger.info("Setting up strategy job...")
|
|
160
|
-
logger.debug(
|
|
161
|
-
"Auth mode: %s",
|
|
162
|
-
"credentials"
|
|
163
|
-
if (config.user.username and config.user.password)
|
|
164
|
-
or config.user.refresh_token
|
|
165
|
-
else "missing",
|
|
166
|
-
)
|
|
157
|
+
logger.debug("Auth mode: API key (from system.api_key)")
|
|
167
158
|
await strategy_job.setup()
|
|
168
159
|
|
|
169
160
|
# Execute action
|
|
@@ -62,8 +62,8 @@ def main():
|
|
|
62
62
|
parser.add_argument(
|
|
63
63
|
"--wallets-file",
|
|
64
64
|
type=Path,
|
|
65
|
-
default=Path(__file__).parent.parent.parent / "
|
|
66
|
-
help="Path to
|
|
65
|
+
default=Path(__file__).parent.parent.parent / "config.json",
|
|
66
|
+
help="Path to config.json file",
|
|
67
67
|
)
|
|
68
68
|
parser.add_argument(
|
|
69
69
|
"--override",
|
|
@@ -121,9 +121,9 @@ def main():
|
|
|
121
121
|
print(f" Updated strategy.py with class name: {class_name}")
|
|
122
122
|
|
|
123
123
|
# Generate wallet with label matching directory name (strategy identifier)
|
|
124
|
-
# If
|
|
124
|
+
# If config.json doesn't exist, create it with a main wallet first
|
|
125
125
|
if not args.wallets_file.exists():
|
|
126
|
-
print(" Creating new
|
|
126
|
+
print(" Creating new config.json with main wallet...")
|
|
127
127
|
main_wallet = make_random_wallet()
|
|
128
128
|
main_wallet["label"] = "main"
|
|
129
129
|
write_wallet_to_json(
|
|
@@ -133,7 +133,7 @@ def main():
|
|
|
133
133
|
)
|
|
134
134
|
print(f" Generated main wallet: {main_wallet['address']}")
|
|
135
135
|
|
|
136
|
-
# Generate strategy wallet (will append to existing
|
|
136
|
+
# Generate strategy wallet (will append to existing config.json)
|
|
137
137
|
wallet = make_random_wallet()
|
|
138
138
|
wallet["label"] = dir_name
|
|
139
139
|
write_wallet_to_json(
|
|
@@ -27,7 +27,7 @@ def main():
|
|
|
27
27
|
"--out-dir",
|
|
28
28
|
type=Path,
|
|
29
29
|
default=Path("."),
|
|
30
|
-
help="Output directory for
|
|
30
|
+
help="Output directory for config.json (and keystore files)",
|
|
31
31
|
)
|
|
32
32
|
parser.add_argument(
|
|
33
33
|
"--keystore-password",
|
|
@@ -55,7 +55,7 @@ def main():
|
|
|
55
55
|
args.out_dir.mkdir(parents=True, exist_ok=True)
|
|
56
56
|
|
|
57
57
|
# Load existing wallets
|
|
58
|
-
existing = load_wallets(args.out_dir, "
|
|
58
|
+
existing = load_wallets(args.out_dir, "config.json")
|
|
59
59
|
has_main = any(w.get("label") in ("main", "default") for w in existing)
|
|
60
60
|
|
|
61
61
|
rows: list[dict[str, str]] = []
|
|
@@ -72,7 +72,7 @@ def main():
|
|
|
72
72
|
w["label"] = args.label
|
|
73
73
|
rows.append(w)
|
|
74
74
|
print(f"[{index}] {w['address']} (label: {args.label})")
|
|
75
|
-
write_wallet_to_json(w, out_dir=args.out_dir, filename="
|
|
75
|
+
write_wallet_to_json(w, out_dir=args.out_dir, filename="config.json")
|
|
76
76
|
if args.keystore_password:
|
|
77
77
|
ks = to_keystore_json(w["private_key_hex"], args.keystore_password)
|
|
78
78
|
ks_path = args.out_dir / f"keystore_{w['address']}.json"
|
|
@@ -86,7 +86,7 @@ def main():
|
|
|
86
86
|
rows.append(main_w)
|
|
87
87
|
print(f"[{index}] {main_w['address']} (main)")
|
|
88
88
|
write_wallet_to_json(
|
|
89
|
-
main_w, out_dir=args.out_dir, filename="
|
|
89
|
+
main_w, out_dir=args.out_dir, filename="config.json"
|
|
90
90
|
)
|
|
91
91
|
if args.keystore_password:
|
|
92
92
|
ks = to_keystore_json(
|
|
@@ -133,7 +133,7 @@ def main():
|
|
|
133
133
|
rows.append(w)
|
|
134
134
|
print(f"[{index}] {w['address']} (label: temporary_{next_temp_num})")
|
|
135
135
|
|
|
136
|
-
write_wallet_to_json(w, out_dir=args.out_dir, filename="
|
|
136
|
+
write_wallet_to_json(w, out_dir=args.out_dir, filename="config.json")
|
|
137
137
|
if args.keystore_password:
|
|
138
138
|
ks = to_keystore_json(w["private_key_hex"], args.keystore_password)
|
|
139
139
|
ks_path = args.out_dir / f"keystore_{w['address']}.json"
|
|
@@ -27,7 +27,7 @@ def _find_wallet(wallets: list[dict[str, Any]], label: str) -> dict[str, Any]:
|
|
|
27
27
|
for w in wallets:
|
|
28
28
|
if w.get("label") == label:
|
|
29
29
|
return w
|
|
30
|
-
raise ValueError(f"Wallet label not found in
|
|
30
|
+
raise ValueError(f"Wallet label not found in config.json: {label}")
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def _get_strategy_class(strategy: str):
|
|
@@ -58,7 +58,7 @@ def _get_strategy_class(strategy: str):
|
|
|
58
58
|
async def _run(args: argparse.Namespace) -> int:
|
|
59
59
|
repo_root = Path(__file__).resolve().parents[2]
|
|
60
60
|
wallets_path = (
|
|
61
|
-
Path(args.wallets).resolve() if args.wallets else repo_root / "
|
|
61
|
+
Path(args.wallets).resolve() if args.wallets else repo_root / "config.json"
|
|
62
62
|
)
|
|
63
63
|
config_path = (
|
|
64
64
|
Path(args.config).resolve() if args.config else repo_root / "config.json"
|
|
@@ -123,7 +123,7 @@ def main() -> int:
|
|
|
123
123
|
],
|
|
124
124
|
)
|
|
125
125
|
p.add_argument(
|
|
126
|
-
"--wallets", default=None, help="Path to
|
|
126
|
+
"--wallets", default=None, help="Path to config.json (default: repo root)"
|
|
127
127
|
)
|
|
128
128
|
p.add_argument(
|
|
129
129
|
"--config", default=None, help="Path to config.json (default: repo root)"
|
|
@@ -38,7 +38,7 @@ class BasisSnapshotMixin:
|
|
|
38
38
|
entry_cost_usd: float,
|
|
39
39
|
exit_cost_usd: float,
|
|
40
40
|
) -> dict[str, Any]:
|
|
41
|
-
"""Build the `safe[horizon]` entry matching
|
|
41
|
+
"""Build the `safe[horizon]` entry matching the expected output shape."""
|
|
42
42
|
L = max(1, int(leverage))
|
|
43
43
|
|
|
44
44
|
depth_checks = depth_checks or {}
|