wayfinder-paths 0.1.3__py3-none-any.whl → 0.1.5__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.

Files changed (109) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +37 -32
  2. wayfinder_paths/__init__.py +3 -3
  3. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/README.md +12 -12
  4. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/adapter.py +12 -11
  5. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/examples.json +1 -1
  6. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/manifest.yaml +1 -1
  7. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/test_adapter.py +12 -6
  8. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/README.md +2 -2
  9. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/adapter.py +30 -23
  10. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/manifest.yaml +1 -1
  11. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/test_adapter.py +2 -2
  12. wayfinder_paths/adapters/hyperlend_adapter/__init__.py +7 -0
  13. wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/adapter.py +33 -26
  14. wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/manifest.yaml +1 -1
  15. wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/test_adapter.py +2 -2
  16. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/README.md +27 -40
  17. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/adapter.py +78 -75
  18. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/examples.json +10 -4
  19. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +11 -0
  20. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/test_adapter.py +33 -28
  21. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/README.md +2 -14
  22. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/adapter.py +12 -19
  23. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/manifest.yaml +1 -1
  24. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/test_adapter.py +2 -2
  25. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/README.md +1 -1
  26. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/adapter.py +8 -4
  27. wayfinder_paths/adapters/token_adapter/examples.json +26 -0
  28. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/manifest.yaml +1 -1
  29. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/test_adapter.py +1 -1
  30. wayfinder_paths/config.example.json +3 -1
  31. wayfinder_paths/core/__init__.py +3 -3
  32. wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
  33. wayfinder_paths/core/adapters/models.py +41 -0
  34. wayfinder_paths/core/clients/BRAPClient.py +21 -2
  35. wayfinder_paths/core/clients/ClientManager.py +42 -63
  36. wayfinder_paths/core/clients/HyperlendClient.py +46 -5
  37. wayfinder_paths/core/clients/LedgerClient.py +350 -124
  38. wayfinder_paths/core/clients/PoolClient.py +51 -19
  39. wayfinder_paths/core/clients/SimulationClient.py +16 -4
  40. wayfinder_paths/core/clients/TokenClient.py +34 -18
  41. wayfinder_paths/core/clients/TransactionClient.py +18 -2
  42. wayfinder_paths/core/clients/WalletClient.py +35 -4
  43. wayfinder_paths/core/clients/WayfinderClient.py +16 -5
  44. wayfinder_paths/core/clients/protocols.py +69 -62
  45. wayfinder_paths/core/clients/sdk_example.py +0 -5
  46. wayfinder_paths/core/config.py +192 -103
  47. wayfinder_paths/core/constants/base.py +17 -0
  48. wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
  49. wayfinder_paths/core/engine/__init__.py +2 -2
  50. wayfinder_paths/core/engine/manifest.py +1 -1
  51. wayfinder_paths/core/services/base.py +6 -4
  52. wayfinder_paths/core/services/local_evm_txn.py +3 -2
  53. wayfinder_paths/core/settings.py +2 -2
  54. wayfinder_paths/core/strategies/Strategy.py +123 -37
  55. wayfinder_paths/core/utils/evm_helpers.py +12 -10
  56. wayfinder_paths/core/wallets/README.md +3 -3
  57. wayfinder_paths/core/wallets/WalletManager.py +3 -3
  58. wayfinder_paths/{vaults/policies → policies}/enso.py +1 -1
  59. wayfinder_paths/{vaults/policies → policies}/hyper_evm.py +2 -2
  60. wayfinder_paths/{vaults/policies → policies}/hyperlend.py +1 -1
  61. wayfinder_paths/{vaults/policies → policies}/moonwell.py +1 -1
  62. wayfinder_paths/{vaults/policies → policies}/prjx.py +1 -1
  63. wayfinder_paths/run_strategy.py +29 -27
  64. wayfinder_paths/scripts/create_strategy.py +3 -3
  65. wayfinder_paths/scripts/make_wallets.py +6 -6
  66. wayfinder_paths/scripts/validate_manifests.py +2 -2
  67. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/README.md +10 -9
  68. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/manifest.yaml +1 -1
  69. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/strategy.py +47 -167
  70. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/test_strategy.py +10 -8
  71. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/README.md +15 -14
  72. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/manifest.yaml +2 -2
  73. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/strategy.py +97 -97
  74. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/test_strategy.py +8 -8
  75. wayfinder_paths/{vaults/templates → templates}/adapter/README.md +5 -5
  76. wayfinder_paths/{vaults/templates → templates}/adapter/manifest.yaml +1 -1
  77. wayfinder_paths/{vaults/templates → templates}/adapter/test_adapter.py +1 -1
  78. wayfinder_paths/{vaults/templates → templates}/strategy/README.md +10 -9
  79. wayfinder_paths/{vaults/templates → templates}/strategy/manifest.yaml +1 -1
  80. wayfinder_paths/{vaults/templates → templates}/strategy/test_strategy.py +8 -8
  81. wayfinder_paths/tests/test_test_coverage.py +5 -5
  82. {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/METADATA +146 -69
  83. wayfinder_paths-0.1.5.dist-info/RECORD +126 -0
  84. wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +0 -7
  85. wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +0 -11
  86. wayfinder_paths/vaults/adapters/token_adapter/examples.json +0 -26
  87. wayfinder_paths/vaults/strategies/__init__.py +0 -0
  88. wayfinder_paths-0.1.3.dist-info/RECORD +0 -126
  89. /wayfinder_paths/{vaults → adapters}/__init__.py +0 -0
  90. /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/__init__.py +0 -0
  91. /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/examples.json +0 -0
  92. /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/__init__.py +0 -0
  93. /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/__init__.py +0 -0
  94. /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/examples.json +0 -0
  95. /wayfinder_paths/{vaults/adapters → adapters}/token_adapter/__init__.py +0 -0
  96. /wayfinder_paths/{vaults/policies → policies}/erc20.py +0 -0
  97. /wayfinder_paths/{vaults/policies → policies}/evm.py +0 -0
  98. /wayfinder_paths/{vaults/policies → policies}/hyperliquid.py +0 -0
  99. /wayfinder_paths/{vaults/policies → policies}/util.py +0 -0
  100. /wayfinder_paths/{vaults/adapters → strategies}/__init__.py +0 -0
  101. /wayfinder_paths/{vaults/strategies → strategies}/config.py +0 -0
  102. /wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/examples.json +0 -0
  103. /wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/examples.json +0 -0
  104. /wayfinder_paths/{vaults/templates → templates}/adapter/adapter.py +0 -0
  105. /wayfinder_paths/{vaults/templates → templates}/adapter/examples.json +0 -0
  106. /wayfinder_paths/{vaults/templates → templates}/strategy/examples.json +0 -0
  107. /wayfinder_paths/{vaults/templates → templates}/strategy/strategy.py +0 -0
  108. {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/LICENSE +0 -0
  109. {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/WHEEL +0 -0
@@ -9,6 +9,15 @@ from dataclasses import dataclass, field
9
9
  from pathlib import Path
10
10
  from typing import Any
11
11
 
12
+ from loguru import logger
13
+
14
+ from wayfinder_paths.core.constants.base import (
15
+ ADAPTER_BALANCE,
16
+ ADAPTER_BRAP,
17
+ ADAPTER_HYPERLIQUID,
18
+ ADAPTER_MOONWELL,
19
+ )
20
+
12
21
 
13
22
  @dataclass
14
23
  class UserConfig:
@@ -24,7 +33,7 @@ class UserConfig:
24
33
 
25
34
  # Wallet configuration
26
35
  main_wallet_address: str | None = None # User's main wallet address
27
- vault_wallet_address: str | None = None # Dedicated vault wallet address
36
+ strategy_wallet_address: str | None = None # Dedicated strategy wallet address
28
37
 
29
38
  # Optional user preferences
30
39
  default_slippage: float = 0.005 # Default slippage tolerance (0.5%)
@@ -38,7 +47,7 @@ class UserConfig:
38
47
  password=data.get("password"),
39
48
  refresh_token=data.get("refresh_token"),
40
49
  main_wallet_address=data.get("main_wallet_address"),
41
- vault_wallet_address=data.get("vault_wallet_address"),
50
+ strategy_wallet_address=data.get("strategy_wallet_address"),
42
51
  default_slippage=data.get("default_slippage", 0.005),
43
52
  gas_multiplier=data.get("gas_multiplier", 1.2),
44
53
  )
@@ -50,7 +59,7 @@ class UserConfig:
50
59
  "password": self.password,
51
60
  "refresh_token": self.refresh_token,
52
61
  "main_wallet_address": self.main_wallet_address,
53
- "vault_wallet_address": self.vault_wallet_address,
62
+ "strategy_wallet_address": self.strategy_wallet_address,
54
63
  "default_slippage": self.default_slippage,
55
64
  "gas_multiplier": self.gas_multiplier,
56
65
  }
@@ -72,7 +81,7 @@ class SystemConfig:
72
81
 
73
82
  # Job configuration
74
83
  job_id: str | None = None
75
- job_type: str = "vault"
84
+ job_type: str = "strategy"
76
85
 
77
86
  # Execution settings
78
87
  update_interval: int = 60 # Default update interval in seconds
@@ -98,7 +107,7 @@ class SystemConfig:
98
107
  os.getenv("WAYFINDER_API_URL", "https://api.wayfinder.ai"),
99
108
  ),
100
109
  job_id=data.get("job_id"),
101
- job_type=data.get("job_type", "vault"),
110
+ job_type=data.get("job_type", "strategy"),
102
111
  update_interval=data.get("update_interval", 60),
103
112
  max_retries=data.get("max_retries", 3),
104
113
  retry_delay=data.get("retry_delay", 5),
@@ -127,9 +136,9 @@ class SystemConfig:
127
136
 
128
137
 
129
138
  @dataclass
130
- class VaultConfig:
139
+ class StrategyJobConfig:
131
140
  """
132
- Complete configuration for a vault job
141
+ Complete configuration for a strategy job
133
142
  Combines user and system configurations
134
143
  """
135
144
 
@@ -144,7 +153,7 @@ class VaultConfig:
144
153
  Enrich strategy_config with wallet addresses and private keys from wallets.json.
145
154
 
146
155
  This method automatically loads wallet information from wallets.json to populate
147
- main_wallet and vault_wallet addresses in strategy_config. Only uses wallets
156
+ main_wallet and strategy_wallet addresses in strategy_config. Only uses wallets
148
157
  with exact label matches (no fallbacks).
149
158
 
150
159
  Wallet enrichment is conditional and can be skipped:
@@ -153,103 +162,154 @@ class VaultConfig:
153
162
  - Allows custom wallet providers (Privy/Turnkey) to opt out of file-based enrichment
154
163
 
155
164
  Note:
156
- This method never raises exceptions - all errors are silently caught to
165
+ This method never raises exceptions - all errors are logged but do not
157
166
  prevent config construction failures.
158
167
  """
159
168
  try:
160
169
  if not isinstance(self.strategy_config, dict):
161
170
  self.strategy_config = {}
162
171
 
163
- # Check wallet_type early - skip enrichment if using non-local wallet provider
164
- wallet_type = self.strategy_config.get("wallet_type")
165
- # Also check in main_wallet and vault_wallet configs
166
- if not wallet_type:
167
- main_wallet = self.strategy_config.get("main_wallet")
168
- if isinstance(main_wallet, dict):
169
- wallet_type = main_wallet.get("wallet_type")
170
- if not wallet_type:
171
- vault_wallet = self.strategy_config.get("vault_wallet")
172
- if isinstance(vault_wallet, dict):
173
- wallet_type = vault_wallet.get("wallet_type")
174
-
175
- # Skip wallets.json enrichment if explicitly using non-local wallet provider
172
+ wallet_type = self._get_wallet_type()
176
173
  if wallet_type and wallet_type != "local":
177
174
  return
178
175
 
179
- # Get strategy name if available (for per-strategy wallet lookup)
180
- strategy_name = self.strategy_config.get("_strategy_name")
181
-
182
- # Load wallets from file for enrichment (only for local wallet types)
183
- entries = _read_wallets_file(self.system.wallets_path)
184
- by_label = {}
185
- by_addr = {}
186
- if entries and isinstance(entries, list):
187
- for e in entries:
188
- if isinstance(e, dict):
189
- # Index by label
190
- label = e.get("label")
191
- if isinstance(label, str):
192
- by_label[label] = e
193
- # Index by address
194
- addr = e.get("address")
195
- if isinstance(addr, str):
196
- by_addr[addr.lower()] = e
197
-
198
- # Load main wallet by exact label match only
199
- if "main_wallet" not in self.strategy_config:
200
- main_wallet = by_label.get("main")
201
- if main_wallet:
202
- self.strategy_config["main_wallet"] = {
203
- "address": main_wallet["address"]
204
- }
176
+ by_label, by_addr = self._load_wallets_from_file()
205
177
 
206
- # Load vault wallet by strategy name label match only
207
- if strategy_name and isinstance(strategy_name, str):
208
- strategy_wallet = by_label.get(strategy_name)
209
- if strategy_wallet:
210
- # Use strategy-specific wallet as vault_wallet
211
- if "vault_wallet" not in self.strategy_config:
212
- self.strategy_config["vault_wallet"] = {
213
- "address": strategy_wallet["address"]
214
- }
215
- elif isinstance(self.strategy_config.get("vault_wallet"), dict):
216
- # Ensure address is set if not already
217
- if not self.strategy_config["vault_wallet"].get("address"):
218
- self.strategy_config["vault_wallet"]["address"] = (
219
- strategy_wallet["address"]
220
- )
221
-
222
- # Enrich wallet configs with private keys from wallets.json
223
- # Only enrich private keys if using local wallet type (or defaulting to local)
224
- # This ensures custom wallet providers don't get private keys from files
178
+ self._enrich_wallet_addresses(by_label)
225
179
  if wallet_type in (None, "local"):
226
- try:
227
- for key in ("main_wallet", "vault_wallet"):
228
- wallet_obj = self.strategy_config.get(key)
229
- if isinstance(wallet_obj, dict):
230
- addr = (wallet_obj.get("address") or "").lower()
231
- entry = by_addr.get(addr)
232
- if entry:
233
- pk = entry.get("private_key") or entry.get(
234
- "private_key_hex"
235
- )
236
- if (
237
- pk
238
- and not wallet_obj.get("private_key")
239
- and not wallet_obj.get("private_key_hex")
240
- ):
241
- wallet_obj["private_key_hex"] = pk
242
- except Exception:
243
- pass
244
- except Exception:
180
+ self._enrich_wallet_private_keys(by_addr)
181
+ except Exception as e:
245
182
  # Defensive: never allow config construction to fail on enrichment
246
- pass
183
+ logger.warning(
184
+ f"Failed to enrich strategy config with wallet information: {e}"
185
+ )
186
+
187
+ def _get_wallet_type(self) -> str | None:
188
+ """
189
+ Determine the wallet type from strategy config.
190
+
191
+ Checks strategy_config, main_wallet, and strategy_wallet for wallet_type.
192
+ Returns the first wallet_type found, or None if not specified.
193
+
194
+ Returns:
195
+ Wallet type string or None if not specified.
196
+ """
197
+ wallet_type = self.strategy_config.get("wallet_type")
198
+ if wallet_type:
199
+ return wallet_type
200
+
201
+ main_wallet = self.strategy_config.get("main_wallet")
202
+ if isinstance(main_wallet, dict):
203
+ wallet_type = main_wallet.get("wallet_type")
204
+ if wallet_type:
205
+ return wallet_type
206
+
207
+ strategy_wallet = self.strategy_config.get("strategy_wallet")
208
+ if isinstance(strategy_wallet, dict):
209
+ wallet_type = strategy_wallet.get("wallet_type")
210
+ if wallet_type:
211
+ return wallet_type
212
+
213
+ return None
214
+
215
+ def _load_wallets_from_file(
216
+ self,
217
+ ) -> tuple[dict[str, dict[str, Any]], dict[str, dict[str, Any]]]:
218
+ """
219
+ Load wallets from wallets.json file and index by label and address.
220
+
221
+ Returns:
222
+ Tuple of (by_label, by_addr) dictionaries:
223
+ - by_label: Maps wallet label to wallet entry
224
+ - by_addr: Maps wallet address (lowercase) to wallet entry
225
+ """
226
+ entries = _read_wallets_file(self.system.wallets_path)
227
+ by_label: dict[str, dict[str, Any]] = {}
228
+ by_addr: dict[str, dict[str, Any]] = {}
229
+
230
+ if entries and isinstance(entries, list):
231
+ for e in entries:
232
+ if isinstance(e, dict):
233
+ # Index by label
234
+ label = e.get("label")
235
+ if isinstance(label, str):
236
+ by_label[label] = e
237
+ # Index by address
238
+ addr = e.get("address")
239
+ if isinstance(addr, str):
240
+ by_addr[addr.lower()] = e
241
+
242
+ return by_label, by_addr
243
+
244
+ def _enrich_wallet_addresses(self, by_label: dict[str, dict[str, Any]]) -> None:
245
+ """
246
+ Enrich strategy_config with wallet addresses from wallets.json.
247
+
248
+ Loads main_wallet and strategy_wallet addresses by exact label match.
249
+ Only sets addresses if they are not already present in strategy_config.
250
+
251
+ Args:
252
+ by_label: Dictionary mapping wallet labels to wallet entries.
253
+ """
254
+ # Load main wallet by exact label match only
255
+ if "main_wallet" not in self.strategy_config:
256
+ main_wallet = by_label.get("main")
257
+ if main_wallet:
258
+ self.strategy_config["main_wallet"] = {
259
+ "address": main_wallet["address"]
260
+ }
261
+
262
+ # Load strategy wallet by strategy name label match only
263
+ strategy_name = self.strategy_config.get("_strategy_name")
264
+ if strategy_name and isinstance(strategy_name, str):
265
+ strategy_wallet = by_label.get(strategy_name)
266
+ if strategy_wallet:
267
+ # Use strategy-specific wallet as strategy_wallet
268
+ if "strategy_wallet" not in self.strategy_config:
269
+ self.strategy_config["strategy_wallet"] = {
270
+ "address": strategy_wallet["address"]
271
+ }
272
+ elif isinstance(self.strategy_config.get("strategy_wallet"), dict):
273
+ # Ensure address is set if not already
274
+ if not self.strategy_config["strategy_wallet"].get("address"):
275
+ self.strategy_config["strategy_wallet"]["address"] = (
276
+ strategy_wallet["address"]
277
+ )
278
+
279
+ def _enrich_wallet_private_keys(self, by_addr: dict[str, dict[str, Any]]) -> None:
280
+ """
281
+ Enrich wallet configs with private keys from wallets.json.
282
+
283
+ Only enriches private keys if using local wallet type (or defaulting to local).
284
+ This ensures custom wallet providers don't get private keys from files.
285
+
286
+ Args:
287
+ by_addr: Dictionary mapping wallet addresses (lowercase) to wallet entries.
288
+ """
289
+ try:
290
+ for key in ("main_wallet", "strategy_wallet"):
291
+ wallet_obj = self.strategy_config.get(key)
292
+ if isinstance(wallet_obj, dict):
293
+ addr = (wallet_obj.get("address") or "").lower()
294
+ entry = by_addr.get(addr)
295
+ if entry:
296
+ pk = entry.get("private_key") or entry.get("private_key_hex")
297
+ if (
298
+ pk
299
+ and not wallet_obj.get("private_key")
300
+ and not wallet_obj.get("private_key_hex")
301
+ ):
302
+ wallet_obj["private_key_hex"] = pk
303
+ except Exception as e:
304
+ logger.warning(
305
+ f"Failed to enrich wallet private keys from wallets.json: {e}"
306
+ )
247
307
 
248
308
  @classmethod
249
309
  def from_dict(
250
310
  cls, data: dict[str, Any], strategy_name: str | None = None
251
- ) -> "VaultConfig":
252
- """Create VaultConfig from dictionary
311
+ ) -> "StrategyJobConfig":
312
+ """Create StrategyJobConfig from dictionary
253
313
 
254
314
  Args:
255
315
  data: Configuration dictionary
@@ -287,14 +347,19 @@ class VaultConfig:
287
347
 
288
348
  # Add wallet configuration if needed
289
349
  # Only use wallets from strategy_config (matched by label) - no fallbacks
290
- if adapter_name in ["balance", "brap", "moonwell", "hyperliquid"]:
291
- vault_wallet = self.strategy_config.get("vault_wallet")
350
+ if adapter_name in [
351
+ ADAPTER_BALANCE,
352
+ ADAPTER_BRAP,
353
+ ADAPTER_MOONWELL,
354
+ ADAPTER_HYPERLIQUID,
355
+ ]:
356
+ strategy_wallet = self.strategy_config.get("strategy_wallet")
292
357
  main_wallet = self.strategy_config.get("main_wallet")
293
- config["vault_wallet"] = (
294
- {"address": vault_wallet["address"]}
295
- if vault_wallet
296
- and isinstance(vault_wallet, dict)
297
- and vault_wallet.get("address")
358
+ config["strategy_wallet"] = (
359
+ {"address": strategy_wallet["address"]}
360
+ if strategy_wallet
361
+ and isinstance(strategy_wallet, dict)
362
+ and strategy_wallet.get("address")
298
363
  else {}
299
364
  )
300
365
  config["main_wallet"] = (
@@ -304,13 +369,13 @@ class VaultConfig:
304
369
  and main_wallet.get("address")
305
370
  else {}
306
371
  )
307
- # user_wallet uses vault_wallet if available, otherwise main_wallet
372
+ # user_wallet uses strategy_wallet if available, otherwise main_wallet
308
373
  config["user_wallet"] = (
309
- config.get("vault_wallet") or config.get("main_wallet") or {}
374
+ config.get("strategy_wallet") or config.get("main_wallet") or {}
310
375
  )
311
376
 
312
377
  # Add specific settings
313
- if adapter_name == "brap":
378
+ if adapter_name == ADAPTER_BRAP:
314
379
  config["default_slippage"] = self.user.default_slippage
315
380
  config["gas_multiplier"] = self.user.gas_multiplier
316
381
 
@@ -321,7 +386,7 @@ class VaultConfig:
321
386
  return config
322
387
 
323
388
 
324
- def load_config_from_env() -> VaultConfig:
389
+ def load_config_from_env() -> StrategyJobConfig:
325
390
  """
326
391
  Load configuration from environment variables
327
392
  This is the simplest way for users to provide configuration
@@ -331,7 +396,7 @@ def load_config_from_env() -> VaultConfig:
331
396
  password=os.getenv("WAYFINDER_PASSWORD"),
332
397
  refresh_token=os.getenv("WAYFINDER_REFRESH_TOKEN"),
333
398
  main_wallet_address=os.getenv("MAIN_WALLET_ADDRESS"),
334
- vault_wallet_address=os.getenv("VAULT_WALLET_ADDRESS"),
399
+ strategy_wallet_address=os.getenv("STRATEGY_WALLET_ADDRESS"),
335
400
  default_slippage=float(os.getenv("DEFAULT_SLIPPAGE", "0.005")),
336
401
  gas_multiplier=float(os.getenv("GAS_MULTIPLIER", "1.2")),
337
402
  )
@@ -348,13 +413,33 @@ def load_config_from_env() -> VaultConfig:
348
413
 
349
414
  # No auto-population - wallets must be explicitly set in environment or matched by label
350
415
 
351
- return VaultConfig(user=user_config, system=system_config)
416
+ return StrategyJobConfig(user=user_config, system=system_config)
352
417
 
353
418
 
354
419
  # --- Internal helpers -------------------------------------------------------
355
420
 
356
421
 
357
422
  def _read_wallets_file(wallets_path: str | None) -> list[dict[str, Any]]:
423
+ """
424
+ Read wallet entries from a JSON file.
425
+
426
+ Args:
427
+ wallets_path: Path to the wallets.json file. If None or empty, returns empty list.
428
+
429
+ Returns:
430
+ List of wallet dictionaries. Each wallet dict should contain:
431
+ - label: Wallet label (str)
432
+ - address: Wallet address (str)
433
+ - private_key or private_key_hex: Private key (str, optional)
434
+
435
+ Returns empty list if file doesn't exist, is invalid JSON, or contains
436
+ non-list data.
437
+
438
+ Note:
439
+ All errors are logged but do not raise exceptions. This allows the
440
+ config system to continue functioning even if wallets.json is missing
441
+ or malformed.
442
+ """
358
443
  if not wallets_path:
359
444
  return []
360
445
  path = Path(wallets_path)
@@ -365,5 +450,9 @@ def _read_wallets_file(wallets_path: str | None) -> list[dict[str, Any]]:
365
450
  if isinstance(data, list):
366
451
  return data
367
452
  return []
368
- except Exception:
453
+ except (FileNotFoundError, json.JSONDecodeError, OSError) as e:
454
+ logger.warning(f"Failed to read wallets file at {wallets_path}: {e}")
455
+ return []
456
+ except Exception as e:
457
+ logger.warning(f"Unexpected error reading wallets file at {wallets_path}: {e}")
369
458
  return []
@@ -23,3 +23,20 @@ DEFAULT_GAS_ESTIMATE_FALLBACK = 100000
23
23
  GAS_BUFFER_MULTIPLIER = 1.1 # 10% buffer for native sends
24
24
  ONE_GWEI = 1_000_000_000
25
25
  DEFAULT_SLIPPAGE = 0.005
26
+
27
+ # Timeout constants (seconds)
28
+ DEFAULT_TRANSACTION_TIMEOUT = 120 # Transaction receipt wait timeout
29
+ DEFAULT_HTTP_TIMEOUT = 30.0 # HTTP client timeout
30
+
31
+ # Adapter type identifiers
32
+ ADAPTER_BALANCE = "BALANCE"
33
+ ADAPTER_BRAP = "BRAP"
34
+ ADAPTER_MOONWELL = "MOONWELL"
35
+ ADAPTER_HYPERLIQUID = "HYPERLIQUID"
36
+ ADAPTER_POOL = "POOL"
37
+ ADAPTER_TOKEN = "TOKEN"
38
+ ADAPTER_LEDGER = "LEDGER"
39
+ ADAPTER_HYPERLEND = "HYPERLEND"
40
+
41
+ # Pagination defaults
42
+ DEFAULT_PAGINATION_LIMIT = 50 # Default limit for paginated API responses
@@ -5,25 +5,25 @@ from typing import Any
5
5
  from loguru import logger
6
6
 
7
7
  from wayfinder_paths.core.clients.ClientManager import ClientManager
8
- from wayfinder_paths.core.config import VaultConfig
8
+ from wayfinder_paths.core.config import StrategyJobConfig
9
9
  from wayfinder_paths.core.strategies.Strategy import Strategy
10
10
 
11
11
 
12
- class VaultJob:
12
+ class StrategyJob:
13
13
  def __init__(
14
14
  self,
15
15
  strategy: Strategy,
16
- config: VaultConfig,
16
+ config: StrategyJobConfig,
17
17
  clients: dict[str, Any] | None = None,
18
18
  skip_auth: bool = False,
19
19
  api_key: str | None = None,
20
20
  ):
21
21
  """
22
- Initialize a VaultJob.
22
+ Initialize a StrategyJob.
23
23
 
24
24
  Args:
25
25
  strategy: The strategy to execute.
26
- config: Vault configuration.
26
+ config: Strategy job configuration.
27
27
  clients: Optional dict of pre-instantiated clients to inject directly.
28
28
  skip_auth: If True, skips authentication (for SDK usage).
29
29
  api_key: Optional API key for service account authentication.
@@ -40,7 +40,7 @@ class VaultJob:
40
40
  def _setup_strategy(self):
41
41
  """Setup the strategy instance"""
42
42
  if not self.strategy:
43
- raise ValueError("No strategy provided to VaultJob")
43
+ raise ValueError("No strategy provided to StrategyJob")
44
44
 
45
45
  self.strategy.log = self.log
46
46
 
@@ -63,7 +63,7 @@ class VaultJob:
63
63
 
64
64
  async def setup(self):
65
65
  """
66
- Initialize the vault job and strategy.
66
+ Initialize the strategy job and strategy.
67
67
 
68
68
  Sets up authentication and initializes the strategy with merged configuration.
69
69
  """
@@ -103,8 +103,8 @@ class VaultJob:
103
103
  ) from e
104
104
 
105
105
  existing_cfg = dict(getattr(self.strategy, "config", {}) or {})
106
- vault_cfg = dict(self.config.strategy_config or {})
107
- merged_cfg = {**vault_cfg, **existing_cfg}
106
+ strategy_cfg = dict(self.config.strategy_config or {})
107
+ merged_cfg = {**strategy_cfg, **existing_cfg}
108
108
  self.strategy.config = merged_cfg
109
109
  self.strategy.clients = self.clients
110
110
  await self.strategy.setup()
@@ -160,23 +160,29 @@ class VaultJob:
160
160
  logger.error(f"Error in continuous execution: {str(e)}")
161
161
  await asyncio.sleep(interval)
162
162
 
163
- async def message_user(self, msg):
164
- if msg is not None:
165
- logger.info(f"notifying user: {msg}")
166
- await self.clients.chat.send_msg_to_owner(
167
- msg, "agent", self.clients.chat.session_id
168
- )
169
-
170
163
  async def log(self, msg: str):
171
164
  """Log messages for the job"""
172
165
  logger.info(f"Job {self.job_id}: {msg}")
173
166
 
174
- async def handle_error(self, error_data):
167
+ async def handle_error(self, error_data: dict[str, Any]) -> None:
168
+ """
169
+ Handle errors that occur during strategy execution.
170
+
171
+ Args:
172
+ error_data: Dictionary containing error information. Expected keys:
173
+ - error: Error message or exception string
174
+ - action: Strategy action that failed (e.g., "deposit", "update")
175
+
176
+ Note:
177
+ Base implementation is a no-op. Subclasses or external systems
178
+ can override this method to implement custom error handling,
179
+ logging, alerting, or recovery logic.
180
+ """
175
181
  pass
176
182
 
177
183
  async def stop(self):
178
- """Stop the vault job and cleanup"""
184
+ """Stop the strategy job and cleanup"""
179
185
  if hasattr(self.strategy, "stop"):
180
186
  await self.strategy.stop()
181
187
 
182
- logger.info(f"Vault job {self.job_id} stopped")
188
+ logger.info(f"Strategy job {self.job_id} stopped")
@@ -1,5 +1,5 @@
1
1
  """Core Engine Module"""
2
2
 
3
- from .VaultJob import VaultJob
3
+ from .StrategyJob import StrategyJob
4
4
 
5
- __all__ = ["VaultJob", "GLOBAL_IMPORTS"]
5
+ __all__ = ["StrategyJob", "GLOBAL_IMPORTS"]
@@ -40,7 +40,7 @@ class StrategyManifest(BaseModel):
40
40
  schema_version: str = Field(default="0.1")
41
41
  entrypoint: str = Field(
42
42
  ...,
43
- description="Python path to class, e.g. vaults.strategies.funding_rate_strategy.FundingRateStrategy",
43
+ description="Python path to class, e.g. strategies.funding_rate_strategy.FundingRateStrategy",
44
44
  )
45
45
  name: str | None = Field(
46
46
  default=None,
@@ -3,6 +3,8 @@ from typing import Any
3
3
 
4
4
  from web3 import AsyncWeb3
5
5
 
6
+ from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
7
+
6
8
 
7
9
  class TokenTxn(ABC):
8
10
  """Interface describing high-level EVM transaction builders."""
@@ -43,7 +45,7 @@ class EvmTxn(ABC):
43
45
  Abstract base class for wallet providers.
44
46
 
45
47
  This interface abstracts all blockchain interactions needed by adapters so the
46
- rest of the vaults codebase never touches raw web3 primitives. Implementations
48
+ rest of the codebase never touches raw web3 primitives. Implementations
47
49
  are responsible for RPC resolution, gas estimation, signing, broadcasting and
48
50
  transaction confirmations.
49
51
  """
@@ -77,7 +79,7 @@ class EvmTxn(ABC):
77
79
  from_address: str,
78
80
  chain_id: int,
79
81
  wait_for_receipt: bool = True,
80
- timeout: int = 120,
82
+ timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
81
83
  ) -> tuple[bool, Any]:
82
84
  """
83
85
  Approve a spender to spend tokens on behalf of from_address.
@@ -103,7 +105,7 @@ class EvmTxn(ABC):
103
105
  transaction: dict[str, Any],
104
106
  *,
105
107
  wait_for_receipt: bool = True,
106
- timeout: int = 120,
108
+ timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
107
109
  ) -> tuple[bool, Any]:
108
110
  """
109
111
  Sign and broadcast a transaction dict.
@@ -165,7 +167,7 @@ class Web3Service(ABC):
165
167
  transaction: dict[str, Any],
166
168
  *,
167
169
  wait_for_receipt: bool = True,
168
- timeout: int = 120,
170
+ timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
169
171
  ) -> tuple[bool, Any]:
170
172
  """Proxy convenience wrapper to underlying wallet provider."""
171
173
  return await self.evm_transactions.broadcast_transaction(
@@ -11,6 +11,7 @@ from wayfinder_paths.core.constants import (
11
11
  ONE_GWEI,
12
12
  ZERO_ADDRESS,
13
13
  )
14
+ from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
14
15
  from wayfinder_paths.core.constants.erc20_abi import (
15
16
  ERC20_APPROVAL_ABI,
16
17
  ERC20_MINIMAL_ABI,
@@ -142,7 +143,7 @@ class LocalEvmTxn(EvmTxn):
142
143
  from_address: str,
143
144
  chain_id: int,
144
145
  wait_for_receipt: bool = True,
145
- timeout: int = 120,
146
+ timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
146
147
  ) -> tuple[bool, Any]:
147
148
  """
148
149
  Approve a spender to spend tokens on behalf of from_address.
@@ -185,7 +186,7 @@ class LocalEvmTxn(EvmTxn):
185
186
  transaction: dict[str, Any],
186
187
  *,
187
188
  wait_for_receipt: bool = True,
188
- timeout: int = 120,
189
+ timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
189
190
  ) -> tuple[bool, Any]:
190
191
  """
191
192
  Sign and broadcast a transaction dict.
@@ -7,7 +7,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
7
7
 
8
8
  class CoreSettings(BaseSettings):
9
9
  """
10
- Core settings for Wayfinder Vaults Engine
10
+ Core settings for Wayfinder Paths Engine
11
11
  These are minimal settings required by the core engine
12
12
  """
13
13
 
@@ -51,7 +51,7 @@ class CoreSettings(BaseSettings):
51
51
 
52
52
  # Logging
53
53
  LOG_LEVEL: str = Field("INFO", env="LOG_LEVEL")
54
- LOG_FILE: str = Field("logs/vault.log", env="LOG_FILE")
54
+ LOG_FILE: str = Field("logs/strategy.log", env="LOG_FILE")
55
55
 
56
56
  # Safety
57
57
  DRY_RUN: bool = Field(False, env="DRY_RUN")