wayfinder-paths 0.1.2__py3-none-any.whl → 0.1.4__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 (83) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +23 -18
  2. wayfinder_paths/abis/generic/erc20.json +383 -0
  3. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/README.md +2 -2
  4. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/adapter.py +2 -2
  5. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/manifest.yaml +1 -1
  6. wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/test_adapter.py +12 -6
  7. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/README.md +1 -1
  8. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/adapter.py +2 -2
  9. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/manifest.yaml +1 -1
  10. wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/test_adapter.py +2 -2
  11. wayfinder_paths/adapters/hyperlend_adapter/__init__.py +7 -0
  12. wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/manifest.yaml +1 -1
  13. wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/test_adapter.py +2 -2
  14. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/README.md +1 -1
  15. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/manifest.yaml +1 -1
  16. wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/test_adapter.py +2 -2
  17. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/README.md +1 -1
  18. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/manifest.yaml +1 -1
  19. wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/test_adapter.py +2 -2
  20. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/README.md +1 -1
  21. wayfinder_paths/adapters/token_adapter/examples.json +26 -0
  22. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/manifest.yaml +1 -1
  23. wayfinder_paths/{vaults/adapters → adapters}/token_adapter/test_adapter.py +1 -1
  24. wayfinder_paths/config.example.json +3 -1
  25. wayfinder_paths/core/engine/manifest.py +1 -1
  26. wayfinder_paths/core/strategies/Strategy.py +3 -3
  27. wayfinder_paths/core/strategies/descriptors.py +80 -0
  28. wayfinder_paths/core/utils/evm_helpers.py +39 -0
  29. wayfinder_paths/policies/enso.py +17 -0
  30. wayfinder_paths/policies/erc20.py +34 -0
  31. wayfinder_paths/policies/evm.py +21 -0
  32. wayfinder_paths/policies/hyper_evm.py +19 -0
  33. wayfinder_paths/policies/hyperlend.py +12 -0
  34. wayfinder_paths/policies/hyperliquid.py +30 -0
  35. wayfinder_paths/policies/moonwell.py +54 -0
  36. wayfinder_paths/policies/prjx.py +30 -0
  37. wayfinder_paths/policies/util.py +27 -0
  38. wayfinder_paths/run_strategy.py +3 -3
  39. wayfinder_paths/scripts/create_strategy.py +3 -3
  40. wayfinder_paths/scripts/validate_manifests.py +2 -2
  41. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/README.md +4 -3
  42. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/manifest.yaml +1 -1
  43. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/strategy.py +143 -81
  44. wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/test_strategy.py +4 -2
  45. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/README.md +4 -3
  46. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/manifest.yaml +1 -1
  47. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/strategy.py +117 -80
  48. wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/test_strategy.py +2 -2
  49. wayfinder_paths/{vaults/templates → templates}/adapter/README.md +4 -4
  50. wayfinder_paths/{vaults/templates → templates}/adapter/manifest.yaml +1 -1
  51. wayfinder_paths/{vaults/templates → templates}/strategy/README.md +6 -5
  52. wayfinder_paths/{vaults/templates → templates}/strategy/manifest.yaml +1 -1
  53. wayfinder_paths/{vaults/templates → templates}/strategy/test_strategy.py +1 -1
  54. {wayfinder_paths-0.1.2.dist-info → wayfinder_paths-0.1.4.dist-info}/METADATA +101 -23
  55. wayfinder_paths-0.1.4.dist-info/RECORD +125 -0
  56. wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +0 -7
  57. wayfinder_paths/vaults/adapters/token_adapter/examples.json +0 -26
  58. wayfinder_paths/vaults/strategies/__init__.py +0 -0
  59. wayfinder_paths-0.1.2.dist-info/RECORD +0 -115
  60. /wayfinder_paths/{vaults → adapters}/__init__.py +0 -0
  61. /wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/examples.json +0 -0
  62. /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/__init__.py +0 -0
  63. /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/examples.json +0 -0
  64. /wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/adapter.py +0 -0
  65. /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/__init__.py +0 -0
  66. /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/adapter.py +0 -0
  67. /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/examples.json +0 -0
  68. /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/__init__.py +0 -0
  69. /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/adapter.py +0 -0
  70. /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/examples.json +0 -0
  71. /wayfinder_paths/{vaults/adapters → adapters}/token_adapter/__init__.py +0 -0
  72. /wayfinder_paths/{vaults/adapters → adapters}/token_adapter/adapter.py +0 -0
  73. /wayfinder_paths/{vaults/adapters → strategies}/__init__.py +0 -0
  74. /wayfinder_paths/{vaults/strategies → strategies}/config.py +0 -0
  75. /wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/examples.json +0 -0
  76. /wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/examples.json +0 -0
  77. /wayfinder_paths/{vaults/templates → templates}/adapter/adapter.py +0 -0
  78. /wayfinder_paths/{vaults/templates → templates}/adapter/examples.json +0 -0
  79. /wayfinder_paths/{vaults/templates → templates}/adapter/test_adapter.py +0 -0
  80. /wayfinder_paths/{vaults/templates → templates}/strategy/examples.json +0 -0
  81. /wayfinder_paths/{vaults/templates → templates}/strategy/strategy.py +0 -0
  82. {wayfinder_paths-0.1.2.dist-info → wayfinder_paths-0.1.4.dist-info}/LICENSE +0 -0
  83. {wayfinder_paths-0.1.2.dist-info → wayfinder_paths-0.1.4.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
1
1
  schema_version: "0.1"
2
- entrypoint: "vaults.strategies.hyperlend_stable_yield_strategy.strategy.HyperlendStableYieldStrategy"
2
+ entrypoint: "strategies.hyperlend_stable_yield_strategy.strategy.HyperlendStableYieldStrategy"
3
3
  permissions:
4
4
  policy: "(wallet.id == 'FORMAT_WALLET_ID')"
5
5
  adapters:
@@ -11,19 +11,42 @@ import pandas as pd
11
11
  from loguru import logger
12
12
  from web3 import Web3
13
13
 
14
+ from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
15
+ from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
16
+ from wayfinder_paths.adapters.hyperlend_adapter.adapter import HyperlendAdapter
17
+ from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
18
+ from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
14
19
  from wayfinder_paths.core.constants.base import DEFAULT_SLIPPAGE
15
20
  from wayfinder_paths.core.services.base import Web3Service
16
21
  from wayfinder_paths.core.services.local_token_txn import (
17
22
  LocalTokenTxnService,
18
23
  )
19
24
  from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
25
+ from wayfinder_paths.core.strategies.descriptors import (
26
+ Complexity,
27
+ Directionality,
28
+ Frequency,
29
+ StratDescriptor,
30
+ TokenExposure,
31
+ Volatility,
32
+ )
20
33
  from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
21
34
  from wayfinder_paths.core.wallets.WalletManager import WalletManager
22
- from wayfinder_paths.vaults.adapters.balance_adapter.adapter import BalanceAdapter
23
- from wayfinder_paths.vaults.adapters.brap_adapter.adapter import BRAPAdapter
24
- from wayfinder_paths.vaults.adapters.hyperlend_adapter.adapter import HyperlendAdapter
25
- from wayfinder_paths.vaults.adapters.ledger_adapter.adapter import LedgerAdapter
26
- from wayfinder_paths.vaults.adapters.token_adapter.adapter import TokenAdapter
35
+ from wayfinder_paths.policies.enso import ENSO_ROUTER, enso_swap
36
+ from wayfinder_paths.policies.erc20 import erc20_spender_for_any_token
37
+ from wayfinder_paths.policies.hyper_evm import (
38
+ hypecore_sentinel_deposit,
39
+ whype_deposit_and_withdraw,
40
+ )
41
+ from wayfinder_paths.policies.hyperlend import (
42
+ HYPERLEND_POOL,
43
+ hyperlend_supply_and_withdraw,
44
+ )
45
+ from wayfinder_paths.policies.hyperliquid import (
46
+ any_hyperliquid_l1_payload,
47
+ any_hyperliquid_user_payload,
48
+ )
49
+ from wayfinder_paths.policies.prjx import PRJX_ROUTER, prjx_swap
27
50
 
28
51
  SYMBOL_TRANSLATION_TABLE = str.maketrans(
29
52
  {
@@ -36,7 +59,7 @@ WRAPPED_HYPE_ADDRESS = "0x5555555555555555555555555555555555555555"
36
59
 
37
60
 
38
61
  class HyperlendStableYieldStrategy(Strategy):
39
- name = "Hyperland Stable Yield Strategy"
62
+ name = "HyperLend Stable Optimizer"
40
63
 
41
64
  # Strategy parameters
42
65
  APY_SHORT_CIRCUIT_THRESHOLD = None
@@ -61,82 +84,110 @@ class HyperlendStableYieldStrategy(Strategy):
61
84
  P_BEST_ROTATION_THRESHOLD = 0.4
62
85
  MAX_CANDIDATES = 5
63
86
  MIN_STABLE_SWAP_TOKENS = 1e-3
64
-
65
- description = f"""Multi-strategy allocator that converts USDT0 into the most consistently rewarding HyperLend stablecoin and continuously checks if a rotation is justified.
66
- **What it does:** Pulls USDT0 from the main wallet, ensures a small HYPE safety buffer for gas, swaps the remaining stable balance into candidate markets, and supplies
67
- liquidity to HyperLend. Hourly rate histories are aggregated into a 7-day panel and routed through a block-bootstrap tournament (horizon {HORIZON_HOURS}h, block length {BLOCK_LEN}, {TRIALS:,}
68
- trials, {HALFLIFE_DAYS}-day half-life weighting) to estimate which asset has the highest probability of outperforming peers. USDT0 is the LayerZero bridgable stablecoin for USDT.
69
- **Exposure type:** Market-neutral stablecoin lending on HyperEVM with automated rotation into whichever pool offers the strongest risk-adjusted lending yield.
70
- **Chains:** HyperEVM only (HyperLend pool suite).
71
- **Deposit/Withdrawal:** Deposits move USDT0 from the main wallet into the strategy wallet, top up a minimal HYPE gas buffer, rotate into the selected stable, and lend it via HyperLend.
72
- Withdrawals unwind the lend position, convert balances back to USDT, and return funds (plus residual HYPE) to the main wallet.
73
- **Gas**: Requires HYPE on HypeEVM. Arbitrary amount of funding gas is accepted via vault wallet transfers.
74
- """
75
- summary = (
76
- "Recency-weighted HyperLend stablecoin optimizer that bootstraps 7-day rate history "
77
- f"(horizon {HORIZON_HOURS}h, {BLOCK_LEN}-hour blocks, {TRIALS:,} simulations) to pick the top "
78
- "performer, funds with USDT0, tops up a small HYPE gas buffer, and defaults to a hysteresis "
79
- f"rotation band (dwell={HYSTERESIS_DWELL_HOURS}h, z={HYSTERESIS_Z:.2f}) to avoid churn while still "
80
- "short-circuiting when yield gaps are extreme."
81
- )
82
-
83
- config = {
84
- "deposit": {
85
- "description": "Move USDT0 into the vault, ensure a small HYPE gas buffer, and supply the best HyperLend stable.",
86
- "parameters": {
87
- "main_token_amount": {
88
- "type": "float",
89
- "unit": "USDT0 tokens",
90
- "description": "Amount of USDT0 to allocate to HyperLend.",
91
- "minimum": 1.0, # TODO: 10
92
- "examples": ["100.0", "250.5"],
93
- },
94
- "gas_token_amount": {
95
- "type": "float",
96
- "unit": "HYPE tokens",
97
- "description": "Amount of HYPE to top up into the vault wallet to cover gas costs.",
98
- "minimum": 0.0,
99
- "maximum": GAS_MAXIMUM,
100
- "recommended": GAS_MAXIMUM,
87
+ MAX_GAS = 0.1 # hype float
88
+
89
+ INFO = StratDescriptor(
90
+ description=f"""Multi-strategy allocator that converts USDT0 into the most consistently rewarding HyperLend stablecoin and continuously checks if a rotation is justified.
91
+ **What it does:** Pulls USDT0 from the main wallet, ensures a small HYPE safety buffer for gas, swaps the remaining stable balance into candidate markets, and supplies
92
+ liquidity to HyperLend. Hourly rate histories are aggregated into a 7-day panel and routed through a block-bootstrap tournament (horizon {HORIZON_HOURS}h, block length {BLOCK_LEN}, {TRIALS:,}
93
+ trials, {HALFLIFE_DAYS}-day half-life weighting) to estimate which asset has the highest probability of outperforming peers. USDT0 is the LayerZero bridgable stablecoin for USDT.
94
+ **Exposure type:** Market-neutral stablecoin lending on HyperEVM with automated rotation into whichever pool offers the strongest risk-adjusted lending yield.
95
+ **Chains:** HyperEVM only (HyperLend pool suite).
96
+ **Deposit/Withdrawal:** Deposits move USDT0 from the main wallet into the strategy wallet, top up a minimal HYPE gas buffer, rotate into the selected stable, and lend it via HyperLend.
97
+ Withdrawals unwind the lend position, convert balances back to USDT, and return funds (plus residual HYPE) to the main wallet.
98
+ **Gas**: Requires HYPE on HypeEVM. Arbitrary amount of funding gas is accepted via vault wallet transfers.
99
+ """,
100
+ summary=(
101
+ "Recency-weighted HyperLend stablecoin optimizer that bootstraps 7-day rate history "
102
+ f"(horizon {HORIZON_HOURS}h, {BLOCK_LEN}-hour blocks, {TRIALS:,} simulations) to pick the top "
103
+ "performer, funds with USDT0, tops up a small HYPE gas buffer, and defaults to a hysteresis "
104
+ f"rotation band (dwell={HYSTERESIS_DWELL_HOURS}h, z={HYSTERESIS_Z:.2f}) to avoid churn while still "
105
+ "short-circuiting when yield gaps are extreme."
106
+ ),
107
+ gas_token_symbol="HYPE",
108
+ gas_token_id="hyperliquid-hyperevm",
109
+ deposit_token_id="usdt0-hyperevm",
110
+ minimum_net_deposit=10,
111
+ gas_maximum=MAX_GAS, # hype float
112
+ gas_threshold=MAX_GAS / 3,
113
+ # risk indicators
114
+ volatility=Volatility.LOW,
115
+ volatility_description_short=(
116
+ "Pure HyperLend stablecoin lending keeps NAV steady aside from rate drift."
117
+ ),
118
+ directionality=Directionality.MARKET_NEUTRAL,
119
+ directionality_description=(
120
+ "Rotates capital between USD stables so exposure stays market neutral."
121
+ ),
122
+ complexity=Complexity.LOW,
123
+ complexity_description="Agent handles optimal pool finding, swaps, and lend transactions automatically.",
124
+ token_exposure=TokenExposure.STABLECOINS,
125
+ token_exposure_description=(
126
+ "Only HyperEVM USD stables (USDT0 and peers), no volatile tokens."
127
+ ),
128
+ frequency=Frequency.LOW,
129
+ frequency_description=(
130
+ "Updates every 2 hours; rotations infrequent (weekly cooldowns)."
131
+ ),
132
+ return_drivers=["lend APY", "pool yield"],
133
+ config={
134
+ "deposit": {
135
+ "description": "Move USDT0 into the vault, ensure a small HYPE gas buffer, and supply the best HyperLend stable.",
136
+ "parameters": {
137
+ "main_token_amount": {
138
+ "type": "float",
139
+ "unit": "USDT0 tokens",
140
+ "description": "Amount of USDT0 to allocate to HyperLend.",
141
+ "minimum": 1.0, # TODO: 10
142
+ "examples": ["100.0", "250.5"],
143
+ },
144
+ "gas_token_amount": {
145
+ "type": "float",
146
+ "unit": "HYPE tokens",
147
+ "description": "Amount of HYPE to top up into the vault wallet to cover gas costs.",
148
+ "minimum": 0.0,
149
+ "maximum": GAS_MAXIMUM,
150
+ "recommended": GAS_MAXIMUM,
151
+ },
101
152
  },
153
+ "result": "USDT0 converted into the top-performing HyperLend stablecoin and supplied on-chain.",
154
+ },
155
+ "withdraw": {
156
+ "description": "Unwinds the position, converts balances to USDT0, and returns funds (plus HYPE buffer) to the main wallet.",
157
+ "parameters": {},
158
+ "result": "Principal and accrued gains returned in USDT0; residual HYPE buffer swept home.",
159
+ },
160
+ "update": {
161
+ "description": (
162
+ "Evaluates tournament projections and rotates when the hysteresis band is breached "
163
+ f"(dwell={HYSTERESIS_DWELL_HOURS}h, z={HYSTERESIS_Z:.2f}) or when a short-circuit gap is hit "
164
+ "(set HYPERLEND_ROTATION_POLICY=cooldown to restore the legacy threshold/cooldown rule)."
165
+ ),
166
+ "parameters": {},
167
+ },
168
+ "status": {
169
+ "description": "Summarises current lend position, APY, and chosen asset.",
170
+ "provides": [
171
+ "lent_asset",
172
+ "lent_balance",
173
+ "current_apy",
174
+ "best_candidate",
175
+ "best_candidate_apy",
176
+ ],
177
+ },
178
+ "points": {
179
+ "description": "Fetch the HyperLend points account snapshot for this vault wallet using a signed login.",
180
+ "parameters": {},
181
+ "result": "Returns the HyperLend points API payload for the strategy wallet.",
182
+ },
183
+ "technical_details": {
184
+ "rotation_policy": ROTATION_POLICY.lower(),
185
+ "hysteresis_dwell_hours": HYSTERESIS_DWELL_HOURS,
186
+ "hysteresis_z": HYSTERESIS_Z,
187
+ "rotation_tx_cost": ROTATION_TX_COST,
102
188
  },
103
- "result": "USDT0 converted into the top-performing HyperLend stablecoin and supplied on-chain.",
104
- },
105
- "withdraw": {
106
- "description": "Unwinds the position, converts balances to USDT0, and returns funds (plus HYPE buffer) to the main wallet.",
107
- "parameters": {},
108
- "result": "Principal and accrued gains returned in USDT0; residual HYPE buffer swept home.",
109
- },
110
- "update": {
111
- "description": (
112
- "Evaluates tournament projections and rotates when the hysteresis band is breached "
113
- f"(dwell={HYSTERESIS_DWELL_HOURS}h, z={HYSTERESIS_Z:.2f}) or when a short-circuit gap is hit "
114
- "(set HYPERLEND_ROTATION_POLICY=cooldown to restore the legacy threshold/cooldown rule)."
115
- ),
116
- "parameters": {},
117
- },
118
- "status": {
119
- "description": "Summarises current lend position, APY, and chosen asset.",
120
- "provides": [
121
- "lent_asset",
122
- "lent_balance",
123
- "current_apy",
124
- "best_candidate",
125
- "best_candidate_apy",
126
- ],
127
- },
128
- "points": {
129
- "description": "Fetch the HyperLend points account snapshot for this vault wallet using a signed login.",
130
- "parameters": {},
131
- "result": "Returns the HyperLend points API payload for the strategy wallet.",
132
- },
133
- "technical_details": {
134
- "rotation_policy": ROTATION_POLICY.lower(),
135
- "hysteresis_dwell_hours": HYSTERESIS_DWELL_HOURS,
136
- "hysteresis_z": HYSTERESIS_Z,
137
- "rotation_tx_cost": ROTATION_TX_COST,
138
189
  },
139
- }
190
+ )
140
191
 
141
192
  def __init__(
142
193
  self,
@@ -2323,6 +2374,17 @@ class HyperlendStableYieldStrategy(Strategy):
2323
2374
  }
2324
2375
 
2325
2376
  @staticmethod
2326
- def policies() -> list[str]:
2377
+ async def policies() -> list[str]:
2327
2378
  """Return policy strings used to scope on-chain permissions."""
2328
- return []
2379
+ return [
2380
+ any_hyperliquid_l1_payload(),
2381
+ any_hyperliquid_user_payload(),
2382
+ hypecore_sentinel_deposit(),
2383
+ await whype_deposit_and_withdraw(),
2384
+ erc20_spender_for_any_token(HYPERLEND_POOL),
2385
+ await hyperlend_supply_and_withdraw(),
2386
+ erc20_spender_for_any_token(ENSO_ROUTER),
2387
+ await enso_swap(),
2388
+ erc20_spender_for_any_token(PRJX_ROUTER),
2389
+ await prjx_swap(),
2390
+ ]
@@ -4,7 +4,7 @@ from unittest.mock import AsyncMock
4
4
 
5
5
  # Ensure wayfinder-paths is on path for tests.test_utils import
6
6
  # This is a workaround until conftest loading order is resolved
7
- _wayfinder_path_dir = Path(__file__).parent.parent.parent.parent.resolve()
7
+ _wayfinder_path_dir = Path(__file__).parent.parent.parent.resolve()
8
8
  _wayfinder_path_str = str(_wayfinder_path_dir)
9
9
  if _wayfinder_path_str not in sys.path:
10
10
  sys.path.insert(0, _wayfinder_path_str)
@@ -29,7 +29,7 @@ except ImportError:
29
29
  get_canonical_examples = test_utils.get_canonical_examples
30
30
  load_strategy_examples = test_utils.load_strategy_examples
31
31
 
32
- from wayfinder_paths.vaults.strategies.hyperlend_stable_yield_strategy.strategy import ( # noqa: E402
32
+ from wayfinder_paths.strategies.hyperlend_stable_yield_strategy.strategy import ( # noqa: E402
33
33
  HyperlendStableYieldStrategy,
34
34
  )
35
35
 
@@ -244,6 +244,8 @@ async def test_smoke(strategy):
244
244
  examples = load_strategy_examples(Path(__file__))
245
245
  smoke_data = examples["smoke"]
246
246
 
247
+ await strategy.setup()
248
+
247
249
  st = await strategy.status()
248
250
  assert isinstance(st, dict)
249
251
  assert "portfolio_value" in st or "net_deposit" in st or "strategy_status" in st
@@ -1,6 +1,6 @@
1
1
  # Stablecoin Yield Strategy
2
2
 
3
- - Entrypoint: `vaults.strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy`
3
+ - Entrypoint: `strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy`
4
4
  - Manifest: `manifest.yaml`
5
5
  - Examples: `examples.json`
6
6
  - Tests: `test_strategy.py`
@@ -72,8 +72,9 @@ Transactions are scoped to the vault wallet and Enso Router approval/swap calls:
72
72
  # Install dependencies
73
73
  poetry install
74
74
 
75
- # Generate default + vault wallets (writes wallets.json)
76
- poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault
75
+ # Generate main wallet (writes wallets.json)
76
+ # Creates a main wallet (or use 'just create-strategy' which auto-creates wallets)
77
+ poetry run python wayfinder_paths/scripts/make_wallets.py -n 1
77
78
 
78
79
  # Copy the example config and set credentials if needed
79
80
  cp wayfinder_paths/config.example.json config.json
@@ -1,5 +1,5 @@
1
1
  schema_version: "0.1"
2
- entrypoint: "vaults.strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy"
2
+ entrypoint: "strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy"
3
3
  permissions:
4
4
  policy: "(wallet.id == 'FORMAT_WALLET_ID') && ((eth.tx.data[0..10] == '0x095ea7b3' && eth.tx.data[34..74] == 'f75584ef6673ad213a685a1b58cc0330b8ea22cf') || (eth.tx.to == '0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf'))"
5
5
  adapters:
@@ -6,18 +6,26 @@ from typing import Any
6
6
 
7
7
  from loguru import logger
8
8
 
9
+ from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
10
+ from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
11
+ from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
12
+ from wayfinder_paths.adapters.pool_adapter.adapter import PoolAdapter
13
+ from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
9
14
  from wayfinder_paths.core.constants.base import DEFAULT_SLIPPAGE
10
15
  from wayfinder_paths.core.services.local_token_txn import (
11
16
  LocalTokenTxnService,
12
17
  )
13
18
  from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
19
+ from wayfinder_paths.core.strategies.descriptors import (
20
+ Complexity,
21
+ Directionality,
22
+ Frequency,
23
+ StratDescriptor,
24
+ TokenExposure,
25
+ Volatility,
26
+ )
14
27
  from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
15
28
  from wayfinder_paths.core.wallets.WalletManager import WalletManager
16
- from wayfinder_paths.vaults.adapters.balance_adapter.adapter import BalanceAdapter
17
- from wayfinder_paths.vaults.adapters.brap_adapter.adapter import BRAPAdapter
18
- from wayfinder_paths.vaults.adapters.ledger_adapter.adapter import LedgerAdapter
19
- from wayfinder_paths.vaults.adapters.pool_adapter.adapter import PoolAdapter
20
- from wayfinder_paths.vaults.adapters.token_adapter.adapter import TokenAdapter
21
29
 
22
30
 
23
31
  class StablecoinYieldStrategy(Strategy):
@@ -33,84 +41,113 @@ class StablecoinYieldStrategy(Strategy):
33
41
  SUPPORTED_NETWORK_CODES = {"base"}
34
42
  ROTATION_MIN_INTERVAL = timedelta(days=14)
35
43
  MINIMUM_APY_IMPROVEMENT = 0.01
36
- GAS_MAXIMUM = 0.02
37
-
38
- description = (
39
- "An automated yield optimization strategy that maximizes returns on USDC deposits on Base.\n\n"
40
- "What it does: Continuously scans and evaluates yield opportunities across Base-based DeFi protocols to find the "
41
- "highest-yielding, low-risk positions for USDC. Automatically rebalances positions when better opportunities "
42
- "emerge to maintain optimal yield generation.\n\n"
43
- "Exposure type: Stable USD-denominated exposure with minimal impermanent loss risk. Focuses exclusively on USDC "
44
- "and operations on the Base network to preserve capital and maximize yield.\n\n"
45
- "Chains: Operates solely on the Base network.\n\n"
46
- f"Deposit/Withdrawal: Accepts deposits only in USDC on Base with a minimum of {MIN_AMOUNT_USDC} USDC. Gas: Requires Base ETH "
47
- "for gas fees during position entry, rebalancing, and exit (~0.001-0.02 ETH per rebalance cycle). Strategy automatically "
48
- "deploys funds to an optimal yield farming position on Base. Withdrawals exit current positions and return USDC to the "
49
- "user wallet.\n\n"
50
- f"Risks: Primary risks include smart contract vulnerabilities in underlying Base DeFi protocols, temporary yield fluctuations, "
51
- f"gas costs during rebalancing, and potential brief capital lock-up during protocol transitions. Strategy filters for a minimum TVL of ${MIN_TVL:,}."
52
- )
53
- summary = (
54
- "Automated stablecoin yield farming across DeFi protocols on Base. "
55
- f"Continuously optimizes positions for maximum stable yield while avoiding impermanent loss. "
56
- f"Min: {MIN_AMOUNT_USDC} USDC + ETH gas. Filters for ${MIN_TVL:,}+ TVL protocols."
57
- )
58
-
59
- # config metadata for UIs/agents
60
- config = {
61
- "deposit": {
62
- "parameters": {
63
- "main_token_amount": {
64
- "type": "float",
65
- "description": "amount of Base USDC (token id: usd-coin-base) to deposit",
66
- },
67
- "gas_token_amount": {
68
- "type": "float",
69
- "description": "amount of Base ETH (token id: ethereum-base) to deposit for gas fees",
70
- "minimum": 0,
71
- "maximum": GAS_MAXIMUM,
44
+ GAS_MAXIMUM = 10e-4 # ethereum float
45
+ GAS_SAFETY_FRACTION = 1 / 3
46
+
47
+ INFO = StratDescriptor(
48
+ description=(
49
+ "An automated yield optimization strategy that maximizes returns on USDC deposits on Base.\n\n"
50
+ "What it does: Continuously scans and evaluates yield opportunities across Base-based DeFi protocols to find the "
51
+ "highest-yielding, low-risk positions for USDC. Automatically rebalances positions when better opportunities "
52
+ "emerge to maintain optimal yield generation.\n\n"
53
+ "Exposure type: Stable USD-denominated exposure with minimal impermanent loss risk. Focuses exclusively on USDC "
54
+ "and operations on the Base network to preserve capital and maximize yield.\n\n"
55
+ "Chains: Operates solely on the Base network.\n\n"
56
+ f"Deposit/Withdrawal: Accepts deposits only in USDC on Base with a minimum of {MIN_AMOUNT_USDC} USDC. Gas: Requires Base ETH "
57
+ "for gas fees during position entry, rebalancing, and exit (~0.001-0.02 ETH per rebalance cycle). Strategy automatically "
58
+ "deploys funds to an optimal yield farming position on Base. Withdrawals exit current positions and return USDC to the "
59
+ "user wallet.\n\n"
60
+ f"Risks: Primary risks include smart contract vulnerabilities in underlying Base DeFi protocols, temporary yield fluctuations, "
61
+ f"gas costs during rebalancing, and potential brief capital lock-up during protocol transitions. Strategy filters for a minimum TVL of ${MIN_TVL:,}."
62
+ ),
63
+ summary=(
64
+ "Automated stablecoin yield farming across DeFi protocols on Base. "
65
+ f"Continuously optimizes positions for maximum stable yield while avoiding impermanent loss. "
66
+ f"Min: {MIN_AMOUNT_USDC} USDC + ETH gas. Filters for ${MIN_TVL:,}+ TVL protocols."
67
+ ),
68
+ gas_token_symbol="ETH",
69
+ gas_token_id="ethereum-base",
70
+ deposit_token_id="usd-coin-base",
71
+ minimum_net_deposit=50,
72
+ gas_maximum=GAS_MAXIMUM,
73
+ # Anything below this level triggers a gas top-up
74
+ gas_threshold=GAS_MAXIMUM * GAS_SAFETY_FRACTION,
75
+ # risk indicators
76
+ volatility=Volatility.LOW,
77
+ volatility_description_short=(
78
+ "Capital sits in Base stablecoin lending pools, so price swings are minimal."
79
+ ),
80
+ directionality=Directionality.MARKET_NEUTRAL,
81
+ directionality_description=(
82
+ "Fully USD-denominated yield farming with no directional crypto beta."
83
+ ),
84
+ complexity=Complexity.LOW,
85
+ complexity_description="Agent handles optimal pool finding and rebalancing",
86
+ token_exposure=TokenExposure.STABLECOINS,
87
+ token_exposure_description=(
88
+ "Only Base USDC (and occasional stable swaps) with no volatile assets."
89
+ ),
90
+ frequency=Frequency.LOW,
91
+ frequency_description=(
92
+ "Updates every 2 hours; rebalances infrequent (bi-weekly cooldowns)."
93
+ ),
94
+ return_drivers=["pool yield"],
95
+ # config metadata for UIs/agents
96
+ config={
97
+ "deposit": {
98
+ "parameters": {
99
+ "main_token_amount": {
100
+ "type": "float",
101
+ "description": "amount of Base USDC (token id: usd-coin-base) to deposit",
102
+ },
103
+ "gas_token_amount": {
104
+ "type": "float",
105
+ "description": "amount of Base ETH (token id: ethereum-base) to deposit for gas fees",
106
+ "minimum": 0,
107
+ "maximum": GAS_MAXIMUM,
108
+ },
72
109
  },
110
+ "process": "Deposits USDC on Base and searches for the highest yield opportunities among Base-based DeFi protocols",
111
+ "requirements": [
112
+ "Sufficient USDC balance on Base",
113
+ "Base ETH available for gas",
114
+ ],
115
+ "result": "Funds deployed to a yield farming position on Base",
116
+ },
117
+ "withdraw": {
118
+ "parameters": {},
119
+ "process": "Exits yield positions on Base and returns USDC to the user wallet",
120
+ "requirements": [
121
+ "Active positions to exit",
122
+ "Gas for transactions on Base",
123
+ ],
124
+ "result": "USDC returned to wallet and positions closed on Base",
125
+ },
126
+ "update": {
127
+ "parameters": {},
128
+ "process": "Scans for better yield opportunities on Base and rebalances positions automatically",
129
+ "frequency": "Call daily or when significant yield changes occur",
130
+ "requirements": [
131
+ "Active strategy positions on Base",
132
+ "Sufficient Base gas for rebalancing",
133
+ ],
134
+ "result": "Positions optimized for maximum yield on Base",
135
+ },
136
+ "technical_details": {
137
+ "wallet_structure": "Uses strategy subwallet for isolation",
138
+ "chains": ["Base"],
139
+ "protocols": ["Various Base DeFi yield protocols"],
140
+ "tokens": ["USDC"],
141
+ "gas_requirements": "~0.001-0.02 ETH per rebalance on Base",
142
+ "search_depth": SEARCH_DEPTH,
143
+ "minimum_tvl": MIN_TVL,
144
+ "dust_apy_threshold": DUST_APY,
145
+ "minimum_apy_edge": MINIMUM_APY_IMPROVEMENT,
146
+ "rotation_cooldown_days": ROTATION_MIN_INTERVAL.days,
147
+ "profit_horizon_days": MINIMUM_DAYS_UNTIL_PROFIT,
73
148
  },
74
- "process": "Deposits USDC on Base and searches for the highest yield opportunities among Base-based DeFi protocols",
75
- "requirements": [
76
- "Sufficient USDC balance on Base",
77
- "Base ETH available for gas",
78
- ],
79
- "result": "Funds deployed to a yield farming position on Base",
80
- },
81
- "withdraw": {
82
- "parameters": {},
83
- "process": "Exits yield positions on Base and returns USDC to the user wallet",
84
- "requirements": [
85
- "Active positions to exit",
86
- "Gas for transactions on Base",
87
- ],
88
- "result": "USDC returned to wallet and positions closed on Base",
89
- },
90
- "update": {
91
- "parameters": {},
92
- "process": "Scans for better yield opportunities on Base and rebalances positions automatically",
93
- "frequency": "Call daily or when significant yield changes occur",
94
- "requirements": [
95
- "Active strategy positions on Base",
96
- "Sufficient Base gas for rebalancing",
97
- ],
98
- "result": "Positions optimized for maximum yield on Base",
99
- },
100
- "technical_details": {
101
- "wallet_structure": "Uses strategy subwallet for isolation",
102
- "chains": ["Base"],
103
- "protocols": ["Various Base DeFi yield protocols"],
104
- "tokens": ["USDC"],
105
- "gas_requirements": "~0.001-0.02 ETH per rebalance on Base",
106
- "search_depth": SEARCH_DEPTH,
107
- "minimum_tvl": MIN_TVL,
108
- "dust_apy_threshold": DUST_APY,
109
- "minimum_apy_edge": MINIMUM_APY_IMPROVEMENT,
110
- "rotation_cooldown_days": ROTATION_MIN_INTERVAL.days,
111
- "profit_horizon_days": MINIMUM_DAYS_UNTIL_PROFIT,
112
149
  },
113
- }
150
+ )
114
151
 
115
152
  def __init__(
116
153
  self,
@@ -4,7 +4,7 @@ from unittest.mock import AsyncMock
4
4
 
5
5
  # Ensure wayfinder-paths is on path for tests.test_utils import
6
6
  # This is a workaround until conftest loading order is resolved
7
- _wayfinder_path_dir = Path(__file__).parent.parent.parent.parent.resolve()
7
+ _wayfinder_path_dir = Path(__file__).parent.parent.parent.resolve()
8
8
  _wayfinder_path_str = str(_wayfinder_path_dir)
9
9
  if _wayfinder_path_str not in sys.path:
10
10
  sys.path.insert(0, _wayfinder_path_str)
@@ -29,7 +29,7 @@ except ImportError:
29
29
  get_canonical_examples = test_utils.get_canonical_examples
30
30
  load_strategy_examples = test_utils.load_strategy_examples
31
31
 
32
- from wayfinder_paths.vaults.strategies.stablecoin_yield_strategy.strategy import ( # noqa: E402
32
+ from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import ( # noqa: E402
33
33
  StablecoinYieldStrategy,
34
34
  )
35
35
 
@@ -8,7 +8,7 @@ Adapters expose protocol-specific capabilities to strategies. They should be thi
8
8
  ```
9
9
  cp -r wayfinder_paths/vaults/templates/adapter wayfinder_paths/vaults/adapters/my_adapter
10
10
  ```
11
- 2. Rename `MyAdapter` in `adapter.py` and update `manifest.yaml` so the `entrypoint` matches (`vaults.adapters.my_adapter.adapter.MyAdapter`).
11
+ 2. Rename `MyAdapter` in `adapter.py` and update `manifest.yaml` so the `entrypoint` matches (`adapters.my_adapter.adapter.MyAdapter`).
12
12
  3. Declare the capabilities your adapter will provide and list any client dependencies (e.g., `PoolClient`, `LedgerClient`).
13
13
  4. Implement the public methods that fulfill those capabilities.
14
14
 
@@ -63,7 +63,7 @@ Every adapter needs a manifest describing its import path, declared capabilities
63
63
 
64
64
  ```yaml
65
65
  schema_version: "0.1"
66
- entrypoint: "vaults.adapters.my_adapter.adapter.MyAdapter"
66
+ entrypoint: "adapters.my_adapter.adapter.MyAdapter"
67
67
  capabilities:
68
68
  - "pool.read"
69
69
  dependencies:
@@ -80,13 +80,13 @@ The `dependencies` list is informational today but helps reviewers understand wh
80
80
  import pytest
81
81
  from unittest.mock import AsyncMock, patch
82
82
 
83
- from wayfinder_paths.vaults.adapters.my_adapter.adapter import MyAdapter
83
+ from wayfinder_paths.adapters.my_adapter.adapter import MyAdapter
84
84
 
85
85
 
86
86
  @pytest.mark.asyncio
87
87
  async def test_get_pools():
88
88
  with patch(
89
- "wayfinder_paths.vaults.adapters.my_adapter.adapter.PoolClient",
89
+ "wayfinder_paths.adapters.my_adapter.adapter.PoolClient",
90
90
  return_value=AsyncMock(
91
91
  get_pools_by_ids=AsyncMock(return_value={"pools": []})
92
92
  ),
@@ -1,5 +1,5 @@
1
1
  schema_version: "0.1"
2
- entrypoint: "vaults.adapters.my_adapter.adapter.MyAdapter"
2
+ entrypoint: "adapters.my_adapter.adapter.MyAdapter"
3
3
  capabilities:
4
4
  - "example.op"
5
5
  dependencies: