wayfinder-paths 0.1.4__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 (60) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +14 -14
  2. wayfinder_paths/__init__.py +3 -3
  3. wayfinder_paths/adapters/balance_adapter/README.md +10 -10
  4. wayfinder_paths/adapters/balance_adapter/adapter.py +10 -9
  5. wayfinder_paths/adapters/balance_adapter/examples.json +1 -1
  6. wayfinder_paths/adapters/brap_adapter/README.md +1 -1
  7. wayfinder_paths/adapters/brap_adapter/adapter.py +28 -21
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +33 -26
  9. wayfinder_paths/adapters/ledger_adapter/README.md +26 -39
  10. wayfinder_paths/adapters/ledger_adapter/adapter.py +78 -75
  11. wayfinder_paths/adapters/ledger_adapter/examples.json +10 -4
  12. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +4 -4
  13. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +31 -26
  14. wayfinder_paths/adapters/pool_adapter/README.md +1 -13
  15. wayfinder_paths/adapters/pool_adapter/adapter.py +12 -19
  16. wayfinder_paths/adapters/token_adapter/adapter.py +8 -4
  17. wayfinder_paths/core/__init__.py +3 -3
  18. wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
  19. wayfinder_paths/core/adapters/models.py +41 -0
  20. wayfinder_paths/core/clients/BRAPClient.py +21 -2
  21. wayfinder_paths/core/clients/ClientManager.py +42 -63
  22. wayfinder_paths/core/clients/HyperlendClient.py +46 -5
  23. wayfinder_paths/core/clients/LedgerClient.py +350 -124
  24. wayfinder_paths/core/clients/PoolClient.py +51 -19
  25. wayfinder_paths/core/clients/SimulationClient.py +16 -4
  26. wayfinder_paths/core/clients/TokenClient.py +34 -18
  27. wayfinder_paths/core/clients/TransactionClient.py +18 -2
  28. wayfinder_paths/core/clients/WalletClient.py +35 -4
  29. wayfinder_paths/core/clients/WayfinderClient.py +16 -5
  30. wayfinder_paths/core/clients/protocols.py +69 -62
  31. wayfinder_paths/core/clients/sdk_example.py +0 -5
  32. wayfinder_paths/core/config.py +192 -103
  33. wayfinder_paths/core/constants/base.py +17 -0
  34. wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
  35. wayfinder_paths/core/engine/__init__.py +2 -2
  36. wayfinder_paths/core/services/base.py +6 -4
  37. wayfinder_paths/core/services/local_evm_txn.py +3 -2
  38. wayfinder_paths/core/settings.py +2 -2
  39. wayfinder_paths/core/strategies/Strategy.py +123 -37
  40. wayfinder_paths/core/utils/evm_helpers.py +12 -10
  41. wayfinder_paths/core/wallets/README.md +3 -3
  42. wayfinder_paths/core/wallets/WalletManager.py +3 -3
  43. wayfinder_paths/run_strategy.py +26 -24
  44. wayfinder_paths/scripts/make_wallets.py +6 -6
  45. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +6 -6
  46. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +36 -156
  47. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +6 -6
  48. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +11 -11
  49. wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +1 -1
  50. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +92 -92
  51. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +6 -6
  52. wayfinder_paths/templates/adapter/README.md +1 -1
  53. wayfinder_paths/templates/adapter/test_adapter.py +1 -1
  54. wayfinder_paths/templates/strategy/README.md +4 -4
  55. wayfinder_paths/templates/strategy/test_strategy.py +7 -7
  56. wayfinder_paths/tests/test_test_coverage.py +5 -5
  57. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/METADATA +46 -47
  58. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/RECORD +60 -59
  59. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/LICENSE +0 -0
  60. {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/WHEEL +0 -0
@@ -1,6 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import traceback
3
5
  from abc import ABC, abstractmethod
6
+ from collections.abc import Awaitable, Callable
4
7
  from typing import Any, TypedDict
5
8
 
6
9
  from loguru import logger
@@ -13,32 +16,53 @@ class StatusDict(TypedDict):
13
16
  portfolio_value: float
14
17
  net_deposit: float
15
18
  strategy_status: Any
19
+ gas_available: float
20
+ gassed_up: bool
16
21
 
17
22
 
18
23
  StatusTuple = tuple[bool, str]
19
24
 
20
25
 
26
+ class WalletConfig(TypedDict, total=False):
27
+ """Wallet configuration structure - allows additional fields for flexibility"""
28
+
29
+ address: str
30
+ private_key: str | None
31
+ private_key_hex: str | None
32
+ wallet_type: str | None
33
+
34
+
35
+ class StrategyConfig(TypedDict, total=False):
36
+ """Base strategy configuration structure - allows additional fields for flexibility"""
37
+
38
+ main_wallet: WalletConfig | None
39
+ strategy_wallet: WalletConfig | None
40
+ wallet_type: str | None
41
+
42
+
21
43
  class Strategy(ABC):
22
- name: str = None
23
- INFO: StratDescriptor = None
44
+ name: str | None = None
45
+ INFO: StratDescriptor | None = None
24
46
 
25
47
  def __init__(
26
48
  self,
27
- config: dict[str, Any] | None = None,
49
+ config: StrategyConfig | dict[str, Any] | None = None,
28
50
  *,
29
- main_wallet: dict[str, Any] | None = None,
30
- vault_wallet: dict[str, Any] | None = None,
51
+ main_wallet: WalletConfig | dict[str, Any] | None = None,
52
+ strategy_wallet: WalletConfig | dict[str, Any] | None = None,
31
53
  simulation: bool = False,
32
- web3_service: Web3Service = None,
54
+ web3_service: Web3Service | None = None,
33
55
  api_key: str | None = None,
34
56
  ):
35
57
  self.adapters = {}
36
- self.ledger = None
58
+ self.ledger_adapter = None
37
59
  self.logger = logger.bind(strategy=self.__class__.__name__)
38
60
  if api_key:
39
61
  os.environ["WAYFINDER_API_KEY"] = api_key
40
62
 
41
- async def setup(self):
63
+ self.config = config
64
+
65
+ async def setup(self) -> None:
42
66
  """Initialize strategy-specific setup after construction"""
43
67
  pass
44
68
 
@@ -46,38 +70,72 @@ class Strategy(ABC):
46
70
  """Log messages - can be overridden by subclasses"""
47
71
  self.logger.info(msg)
48
72
 
49
- async def temp_ui_message(self, msg: str) -> None:
50
- """Hook for temporary UI messages (e.g., progress) to the chat window."""
51
- # No-op by default; strategies/hosts can override
52
- return None
53
-
54
- async def quote(self):
73
+ async def quote(self) -> None:
55
74
  """Get quotes for potential trades - optional for strategies"""
56
75
  pass
57
76
 
77
+ def _get_strategy_wallet_address(self) -> str:
78
+ """Get strategy wallet address with validation."""
79
+ strategy_wallet = self.config.get("strategy_wallet")
80
+ if not strategy_wallet or not isinstance(strategy_wallet, dict):
81
+ raise ValueError("strategy_wallet not configured in strategy config")
82
+ address = strategy_wallet.get("address")
83
+ if not address:
84
+ raise ValueError("strategy_wallet address not found in config")
85
+ return str(address)
86
+
87
+ def _get_main_wallet_address(self) -> str:
88
+ """Get main wallet address with validation."""
89
+ main_wallet = self.config.get("main_wallet")
90
+ if not main_wallet or not isinstance(main_wallet, dict):
91
+ raise ValueError("main_wallet not configured in strategy config")
92
+ address = main_wallet.get("address")
93
+ if not address:
94
+ raise ValueError("main_wallet address not found in config")
95
+ return str(address)
96
+
58
97
  @abstractmethod
59
98
  async def deposit(self, **kwargs) -> StatusTuple:
60
99
  """
61
- Deposit funds into the strategy
62
- Returns: (success: bool, message: str)
100
+ Deposit funds into the strategy.
101
+
102
+ Args:
103
+ **kwargs: Strategy-specific deposit parameters. Common parameters include:
104
+ - main_token_amount: Amount of main token to deposit (float)
105
+ - gas_token_amount: Amount of gas token to deposit (float)
106
+
107
+ Returns:
108
+ Tuple of (success: bool, message: str)
109
+
110
+ Raises:
111
+ ValueError: If required parameters are missing or invalid.
63
112
  """
64
113
  pass
65
114
 
66
115
  async def withdraw(self, **kwargs) -> StatusTuple:
67
116
  """
68
- Withdraw funds from the strategy
69
- Default implementation unwinds all operations
70
- Returns: (success: bool, message: str)
117
+ Withdraw funds from the strategy.
118
+ Default implementation unwinds all operations.
119
+
120
+ Args:
121
+ **kwargs: Strategy-specific withdrawal parameters (optional).
122
+
123
+ Returns:
124
+ Tuple of (success: bool, message: str)
125
+
126
+ Note:
127
+ Subclasses may override this method to add validation or custom
128
+ withdrawal logic. The base implementation unwinds all ledger operations.
71
129
  """
72
- if hasattr(self, "ledger") and self.ledger:
73
- while self.ledger.positions.operations:
74
- node = self.ledger.positions.operations[-1]
130
+ if hasattr(self, "ledger_adapter") and self.ledger_adapter:
131
+ while self.ledger_adapter.positions.operations:
132
+ node = self.ledger_adapter.positions.operations[-1]
75
133
  adapter = self.adapters.get(node.adapter)
76
134
  if adapter and hasattr(adapter, "unwind_op"):
77
135
  await adapter.unwind_op(node)
78
- self.ledger.positions.operations.pop()
136
+ self.ledger_adapter.positions.operations.pop()
79
137
 
80
- await self.ledger.save()
138
+ await self.ledger_adapter.save()
81
139
 
82
140
  return (True, "Withdrawal complete")
83
141
 
@@ -108,7 +166,12 @@ class Strategy(ABC):
108
166
  Wrapper to compute and return strategy status. In Django, this also snapshots.
109
167
  Here we simply delegate to _status for compatibility.
110
168
  """
111
- return await self._status()
169
+
170
+ status = await self._status()
171
+ await self.ledger_adapter.record_strategy_snapshot(
172
+ wallet_address=self._get_strategy_wallet_address(),
173
+ strategy_status=status,
174
+ )
112
175
 
113
176
  def register_adapters(self, adapters: list[Any]) -> None:
114
177
  """Register adapters for use by the strategy"""
@@ -119,13 +182,15 @@ class Strategy(ABC):
119
182
  elif hasattr(adapter, "__class__"):
120
183
  self.adapters[adapter.__class__.__name__] = adapter
121
184
 
122
- def unwind_on_error(self, func):
185
+ def unwind_on_error(
186
+ self, func: Callable[..., Awaitable[StatusTuple]]
187
+ ) -> Callable[..., Awaitable[StatusTuple]]:
123
188
  """
124
189
  Decorator to unwind operations on error
125
190
  Useful for deposit operations that need cleanup on failure
126
191
  """
127
192
 
128
- async def wrapper(*args, **kwargs):
193
+ async def wrapper(*args: Any, **kwargs: Any) -> StatusTuple:
129
194
  try:
130
195
  return await func(*args, **kwargs)
131
196
  except Exception:
@@ -143,21 +208,28 @@ class Strategy(ABC):
143
208
  f"Strategy failed and unwinding also failed. Operation error: {trace}. Unwind error: {trace2}",
144
209
  )
145
210
  finally:
146
- if hasattr(self, "ledger") and self.ledger:
147
- await self.ledger.save()
211
+ if hasattr(self, "ledger_adapter") and self.ledger_adapter:
212
+ await self.ledger_adapter.save()
148
213
 
149
214
  return wrapper
150
215
 
151
216
  @classmethod
152
217
  def get_metadata(cls) -> dict[str, Any]:
153
218
  """
154
- Return metadata about this strategy
155
- Can be overridden to provide discovery information
219
+ Return metadata about this strategy.
220
+ Can be overridden to provide discovery information.
221
+
222
+ Returns:
223
+ Dictionary containing strategy metadata. The following keys are optional
224
+ and will be None if not defined on the class:
225
+ - name: Strategy name
226
+ - description: Strategy description
227
+ - summary: Strategy summary
156
228
  """
157
229
  return {
158
- "name": cls.name,
159
- "description": cls.description,
160
- "summary": cls.summary,
230
+ "name": getattr(cls, "name", None),
231
+ "description": getattr(cls, "description", None),
232
+ "summary": getattr(cls, "summary", None),
161
233
  }
162
234
 
163
235
  async def health_check(self) -> dict[str, Any]:
@@ -176,8 +248,22 @@ class Strategy(ABC):
176
248
 
177
249
  async def partial_liquidate(self, usd_value: float) -> StatusTuple:
178
250
  """
179
- Partially liquidate strategy positions by USD value
180
- Optional method that can be overridden by subclasses
181
- Returns: (success: bool, message: str)
251
+ Partially liquidate strategy positions by USD value.
252
+ Optional method that can be overridden by subclasses.
253
+
254
+ Args:
255
+ usd_value: USD value to liquidate (must be positive).
256
+
257
+ Returns:
258
+ Tuple of (success: bool, message: str)
259
+
260
+ Raises:
261
+ ValueError: If usd_value is not positive.
262
+
263
+ Note:
264
+ Base implementation returns failure. Subclasses should override
265
+ to implement partial liquidation logic.
182
266
  """
267
+ if usd_value <= 0:
268
+ raise ValueError(f"usd_value must be positive, got {usd_value}")
183
269
  return (False, "Partial liquidation not implemented for this strategy")
@@ -130,41 +130,43 @@ def resolve_private_key_for_from_address(
130
130
  """
131
131
  from_addr_norm = (from_address or "").lower()
132
132
  main_wallet = config.get("main_wallet")
133
- vault_wallet = config.get("vault_wallet")
133
+ strategy_wallet = config.get("strategy_wallet")
134
134
 
135
135
  main_pk = None
136
- vault_pk = None
136
+ strategy_pk = None
137
137
  try:
138
138
  if isinstance(main_wallet, dict):
139
139
  main_pk = main_wallet.get("private_key") or main_wallet.get(
140
140
  "private_key_hex"
141
141
  )
142
- if isinstance(vault_wallet, dict):
143
- vault_pk = vault_wallet.get("private_key") or vault_wallet.get(
142
+ if isinstance(strategy_wallet, dict):
143
+ strategy_pk = strategy_wallet.get("private_key") or strategy_wallet.get(
144
144
  "private_key_hex"
145
145
  )
146
146
  except (AttributeError, TypeError) as e:
147
147
  logger.debug("Error resolving private keys from wallet config: %s", e)
148
148
 
149
149
  main_addr = None
150
- vault_addr = None
150
+ strategy_addr = None
151
151
  try:
152
152
  main_addr = (main_wallet or {}).get("address") or (
153
153
  (main_wallet or {}).get("evm") or {}
154
154
  ).get("address")
155
- vault_addr = (vault_wallet or {}).get("address") or (
156
- (vault_wallet or {}).get("evm") or {}
155
+ strategy_addr = (strategy_wallet or {}).get("address") or (
156
+ (strategy_wallet or {}).get("evm") or {}
157
157
  ).get("address")
158
158
  except (AttributeError, TypeError) as e:
159
159
  logger.debug("Error resolving addresses from wallet config: %s", e)
160
160
 
161
161
  if main_addr and from_addr_norm == (main_addr or "").lower():
162
162
  return main_pk or os.getenv("PRIVATE_KEY")
163
- if vault_addr and from_addr_norm == (vault_addr or "").lower():
164
- return vault_pk or os.getenv("PRIVATE_KEY_VAULT") or os.getenv("PRIVATE_KEY")
163
+ if strategy_addr and from_addr_norm == (strategy_addr or "").lower():
164
+ return (
165
+ strategy_pk or os.getenv("PRIVATE_KEY_STRATEGY") or os.getenv("PRIVATE_KEY")
166
+ )
165
167
 
166
168
  # Fallback to environment variables
167
- return os.getenv("PRIVATE_KEY_VAULT") or os.getenv("PRIVATE_KEY")
169
+ return os.getenv("PRIVATE_KEY_STRATEGY") or os.getenv("PRIVATE_KEY")
168
170
 
169
171
 
170
172
  async def _get_abi(chain_id: int, address: str) -> str | None:
@@ -15,7 +15,7 @@ Wayfinder strategies interact with blockchains through a single abstraction: the
15
15
  from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
16
16
  from wayfinder_paths.core.wallets.WalletManager import WalletManager
17
17
 
18
- config = {...} # contains main_wallet / vault_wallet entries
18
+ config = {...} # contains main_wallet / strategy_wallet entries
19
19
  wallet_provider = WalletManager.get_provider(config)
20
20
  web3_service = DefaultWeb3Service(config, wallet_provider=wallet_provider)
21
21
 
@@ -70,14 +70,14 @@ web3_service = DefaultWeb3Service(config, wallet_provider=custom_wallet)
70
70
 
71
71
  ## Configuration hints
72
72
 
73
- `WalletManager.get_provider` looks for `wallet_type` on the top-level config, `main_wallet`, and `vault_wallet`. Example:
73
+ `WalletManager.get_provider` looks for `wallet_type` on the top-level config, `main_wallet`, and `strategy_wallet`. Example:
74
74
 
75
75
  ```json
76
76
  {
77
77
  "strategy": {
78
78
  "wallet_type": "local",
79
79
  "main_wallet": {"address": "0x...", "wallet_type": "local"},
80
- "vault_wallet": {"address": "0x..."}
80
+ "strategy_wallet": {"address": "0x..."}
81
81
  }
82
82
  }
83
83
  ```
@@ -41,9 +41,9 @@ class WalletManager:
41
41
  wallet_type = main_wallet.get("wallet_type")
42
42
 
43
43
  if not wallet_type:
44
- vault_wallet = config.get("vault_wallet")
45
- if isinstance(vault_wallet, dict):
46
- wallet_type = vault_wallet.get("wallet_type")
44
+ strategy_wallet = config.get("strategy_wallet")
45
+ if isinstance(strategy_wallet, dict):
46
+ wallet_type = strategy_wallet.get("wallet_type")
47
47
 
48
48
  if not wallet_type or wallet_type == "local":
49
49
  logger.debug("Using LocalWalletProvider (default)")
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Strategy Runner
4
- Main entry point for running vault strategies locally
4
+ Main entry point for running strategies locally
5
5
  """
6
6
 
7
7
  import argparse
@@ -12,9 +12,9 @@ from pathlib import Path
12
12
 
13
13
  from loguru import logger
14
14
 
15
- from wayfinder_paths.core.config import VaultConfig, load_config_from_env
15
+ from wayfinder_paths.core.config import StrategyJobConfig, load_config_from_env
16
16
  from wayfinder_paths.core.engine.manifest import load_manifest, validate_manifest
17
- from wayfinder_paths.core.engine.VaultJob import VaultJob
17
+ from wayfinder_paths.core.engine.StrategyJob import StrategyJob
18
18
 
19
19
 
20
20
  def load_strategy(
@@ -66,7 +66,7 @@ def load_strategy(
66
66
 
67
67
  def load_config(
68
68
  config_path: str | None = None, strategy_name: str | None = None
69
- ) -> VaultConfig:
69
+ ) -> StrategyJobConfig:
70
70
  """
71
71
  Load configuration from file or environment
72
72
 
@@ -75,13 +75,13 @@ def load_config(
75
75
  strategy_name: Optional strategy name for per-strategy wallet lookup
76
76
 
77
77
  Returns:
78
- VaultConfig instance
78
+ StrategyJobConfig instance
79
79
  """
80
80
  if config_path and Path(config_path).exists():
81
81
  logger.info(f"Loading config from {config_path}")
82
82
  with open(config_path) as f:
83
83
  config_data = json.load(f)
84
- return VaultConfig.from_dict(config_data, strategy_name=strategy_name)
84
+ return StrategyJobConfig.from_dict(config_data, strategy_name=strategy_name)
85
85
  else:
86
86
  logger.info("Loading config from environment variables")
87
87
  config = load_config_from_env()
@@ -100,7 +100,7 @@ async def run_strategy(
100
100
  **kwargs,
101
101
  ):
102
102
  """
103
- Run a vault strategy
103
+ Run a strategy
104
104
 
105
105
  Args:
106
106
  strategy_name: Name of the strategy to run
@@ -141,13 +141,13 @@ async def run_strategy(
141
141
  logger.debug(f"Config path provided: {config_path}")
142
142
  config = load_config(config_path, strategy_name=strategy_name_for_wallet)
143
143
  logger.debug(
144
- "Loaded config: creds=%s wallets(main=%s vault=%s)",
144
+ "Loaded config: creds=%s wallets(main=%s strategy=%s)",
145
145
  "yes"
146
146
  if (config.user.username and config.user.password)
147
147
  or config.user.refresh_token
148
148
  else "no",
149
149
  (config.user.main_wallet_address or "none"),
150
- (config.user.vault_wallet_address or "none"),
150
+ (config.user.strategy_wallet_address or "none"),
151
151
  )
152
152
 
153
153
  # Validate required configuration
@@ -173,11 +173,11 @@ async def run_strategy(
173
173
  )
174
174
  logger.info(f"Loaded strategy: {strategy.name}")
175
175
 
176
- # Create vault job
177
- vault_job = VaultJob(strategy, config)
176
+ # Create strategy job
177
+ strategy_job = StrategyJob(strategy, config)
178
178
 
179
- # Setup vault job
180
- logger.info("Setting up vault job...")
179
+ # Setup strategy job
180
+ logger.info("Setting up strategy job...")
181
181
  logger.debug(
182
182
  "Auth mode: %s",
183
183
  "credentials"
@@ -185,12 +185,12 @@ async def run_strategy(
185
185
  or config.user.refresh_token
186
186
  else "missing",
187
187
  )
188
- await vault_job.setup()
188
+ await strategy_job.setup()
189
189
 
190
190
  # Execute action
191
191
  if action == "run":
192
192
  logger.info("Starting continuous execution...")
193
- await vault_job.run_continuous(interval_seconds=kwargs.get("interval"))
193
+ await strategy_job.run_continuous(interval_seconds=kwargs.get("interval"))
194
194
 
195
195
  elif action == "deposit":
196
196
  main_token_amount = kwargs.get("main_token_amount")
@@ -207,7 +207,7 @@ async def run_strategy(
207
207
  if gas_token_amount is None:
208
208
  gas_token_amount = 0.0
209
209
 
210
- result = await vault_job.execute_strategy(
210
+ result = await strategy_job.execute_strategy(
211
211
  "deposit",
212
212
  main_token_amount=main_token_amount,
213
213
  gas_token_amount=gas_token_amount,
@@ -216,22 +216,22 @@ async def run_strategy(
216
216
 
217
217
  elif action == "withdraw":
218
218
  amount = kwargs.get("amount")
219
- result = await vault_job.execute_strategy("withdraw", amount=amount)
219
+ result = await strategy_job.execute_strategy("withdraw", amount=amount)
220
220
  logger.info(f"Withdraw result: {result}")
221
221
 
222
222
  elif action == "status":
223
- result = await vault_job.execute_strategy("status")
223
+ result = await strategy_job.execute_strategy("status")
224
224
  logger.info(f"Status: {json.dumps(result, indent=2)}")
225
225
 
226
226
  elif action == "update":
227
- result = await vault_job.execute_strategy("update")
227
+ result = await strategy_job.execute_strategy("update")
228
228
  logger.info(f"Update result: {result}")
229
229
 
230
230
  elif action == "partial-liquidate":
231
231
  usd_value = kwargs.get("amount")
232
232
  if not usd_value:
233
233
  raise ValueError("Amount (USD value) required for partial-liquidate")
234
- result = await vault_job.execute_strategy(
234
+ result = await strategy_job.execute_strategy(
235
235
  "partial_liquidate", usd_value=usd_value
236
236
  )
237
237
  logger.info(f"Partial liquidation result: {result}")
@@ -289,7 +289,9 @@ async def run_strategy(
289
289
  duration = kwargs.get("duration") or 300
290
290
  logger.info(f"Running script mode for {duration}s...")
291
291
  task = asyncio.create_task(
292
- vault_job.run_continuous(interval_seconds=kwargs.get("interval") or 60)
292
+ strategy_job.run_continuous(
293
+ interval_seconds=kwargs.get("interval") or 60
294
+ )
293
295
  )
294
296
  await asyncio.sleep(duration)
295
297
  task.cancel()
@@ -307,13 +309,13 @@ async def run_strategy(
307
309
  logger.error(f"Error: {e}")
308
310
  sys.exit(1)
309
311
  finally:
310
- if "vault_job" in locals():
311
- await vault_job.stop()
312
+ if "strategy_job" in locals():
313
+ await strategy_job.stop()
312
314
 
313
315
 
314
316
  def main():
315
317
  """Main entry point"""
316
- parser = argparse.ArgumentParser(description="Run vault strategies")
318
+ parser = argparse.ArgumentParser(description="Run strategy strategies")
317
319
  parser.add_argument(
318
320
  "strategy",
319
321
  nargs="?",
@@ -22,18 +22,18 @@ def write_env(rows: list[dict[str, str]], out_dir: Path) -> None:
22
22
  main_w = (
23
23
  label_to_wallet.get("main") or label_to_wallet.get("default") or rows[0]
24
24
  )
25
- vault_w = label_to_wallet.get("vault")
25
+ strategy_w = label_to_wallet.get("strategy")
26
26
 
27
27
  f.write("RPC_URL=https://rpc.ankr.com/eth\n")
28
28
  # Back-compat defaults
29
29
  f.write(f"PRIVATE_KEY={main_w['private_key_hex']}\n")
30
30
  f.write(f"FROM_ADDRESS={main_w['address']}\n")
31
- # Explicit main/vault variables
31
+ # Explicit main/strategy variables
32
32
  f.write(f"MAIN_WALLET_ADDRESS={main_w['address']}\n")
33
- if vault_w:
34
- f.write(f"VAULT_WALLET_ADDRESS={vault_w['address']}\n")
35
- # Optional: expose vault private key for local dev only
36
- f.write(f"PRIVATE_KEY_VAULT={vault_w['private_key_hex']}\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
37
 
38
38
 
39
39
  def main():
@@ -9,14 +9,14 @@
9
9
 
10
10
  Allocates USDT0 on HyperEVM across HyperLend stablecoin markets. The strategy:
11
11
 
12
- 1. Pulls USDT0 (plus a configurable HYPE gas buffer) from the main wallet into the vault wallet.
12
+ 1. Pulls USDT0 (plus a configurable HYPE gas buffer) from the main wallet into the strategy wallet.
13
13
  2. Samples HyperLend hourly rate history, applies a bootstrap tournament (horizon = 6h, blocks = 6h, 4,000 trials, 7-day half-life) to estimate which stablecoin should outperform.
14
14
  3. Tops up the small HYPE gas buffer if needed, swaps USDT0 into the target stablecoin, and supplies it to HyperLend.
15
15
  4. Enforces a hysteresis rotation policy so minor APY noise does not churn capital.
16
16
 
17
17
  ## Policy
18
18
 
19
- The manifest policy simply locks transactions to the vault wallet ID:
19
+ The manifest policy simply locks transactions to the strategy wallet ID:
20
20
 
21
21
  ```
22
22
  (wallet.id == 'FORMAT_WALLET_ID')
@@ -46,14 +46,14 @@ The manifest policy simply locks transactions to the vault wallet ID:
46
46
  ### Deposit
47
47
 
48
48
  - Validates USDT0 and HYPE balances in the main wallet.
49
- - Transfers HYPE into the vault wallet when a top-up is required, ensuring the vault maintains the configured buffer.
50
- - Moves USDT0 from the main wallet into the vault wallet through `BalanceAdapter.move_from_main_wallet_to_vault_wallet`.
49
+ - Transfers HYPE into the strategy wallet when a top-up is required, ensuring the strategy maintains the configured buffer.
50
+ - Moves USDT0 from the main wallet into the strategy wallet through `BalanceAdapter.move_from_main_wallet_to_strategy_wallet`.
51
51
  - Clears cached asset snapshots so the next update starts from on-chain reality.
52
52
 
53
53
  ### Update
54
54
 
55
55
  - Refreshes HyperLend asset snapshots, calculates tournament winners, and filters markets that respect supply caps + buffer requirements.
56
- - Reads rotation history through `LedgerAdapter.get_vault_latest_transactions` to enforce the cooldown (unless the short-circuit policy is triggered).
56
+ - Reads rotation history through `LedgerAdapter.get_strategy_latest_transactions` to enforce the cooldown (unless the short-circuit policy is triggered).
57
57
  - If a new asset wins the tournament and passes hysteresis checks, BRAP quotes are fetched and executed to rotate into the better performer.
58
58
  - Sweeps residual stable balances, lends via `HyperlendAdapter`, and records ledger operations.
59
59
 
@@ -94,7 +94,7 @@ poetry run python wayfinder_paths/run_strategy.py hyperlend_stable_yield_strateg
94
94
  Use the manifest directly if you prefer:
95
95
 
96
96
  ```bash
97
- poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/manifest.yaml --action status --config $(pwd)/config.json
97
+ poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml --action status --config $(pwd)/config.json
98
98
  ```
99
99
 
100
100
  Wallet addresses/labels are auto-resolved from `wallets.json`. Set `NETWORK=testnet` in your config to run the orchestration without touching live HyperEVM endpoints.