wayfinder-paths 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wayfinder-paths might be problematic. Click here for more details.

Files changed (80) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +6 -15
  2. wayfinder_paths/adapters/balance_adapter/README.md +1 -2
  3. wayfinder_paths/adapters/balance_adapter/adapter.py +4 -4
  4. wayfinder_paths/adapters/brap_adapter/README.md +1 -1
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +139 -74
  6. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -7
  7. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +0 -54
  8. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +1 -1
  9. wayfinder_paths/adapters/ledger_adapter/README.md +1 -1
  10. wayfinder_paths/adapters/moonwell_adapter/README.md +174 -0
  11. wayfinder_paths/adapters/moonwell_adapter/__init__.py +7 -0
  12. wayfinder_paths/adapters/moonwell_adapter/adapter.py +1226 -0
  13. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +635 -0
  14. wayfinder_paths/adapters/pool_adapter/README.md +1 -77
  15. wayfinder_paths/adapters/pool_adapter/adapter.py +0 -122
  16. wayfinder_paths/adapters/pool_adapter/examples.json +0 -57
  17. wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -86
  18. wayfinder_paths/adapters/token_adapter/README.md +1 -1
  19. wayfinder_paths/core/clients/ClientManager.py +1 -22
  20. wayfinder_paths/core/clients/WalletClient.py +0 -8
  21. wayfinder_paths/core/clients/WayfinderClient.py +7 -12
  22. wayfinder_paths/core/clients/__init__.py +0 -8
  23. wayfinder_paths/core/clients/protocols.py +0 -60
  24. wayfinder_paths/core/config.py +5 -45
  25. wayfinder_paths/core/constants/__init__.py +0 -2
  26. wayfinder_paths/core/constants/base.py +6 -2
  27. wayfinder_paths/core/constants/moonwell_abi.py +411 -0
  28. wayfinder_paths/core/services/base.py +7 -1
  29. wayfinder_paths/core/services/local_evm_txn.py +223 -222
  30. wayfinder_paths/core/services/local_token_txn.py +103 -92
  31. wayfinder_paths/core/services/web3_service.py +0 -2
  32. wayfinder_paths/core/settings.py +8 -8
  33. wayfinder_paths/core/strategies/Strategy.py +1 -5
  34. wayfinder_paths/core/strategies/descriptors.py +1 -1
  35. wayfinder_paths/core/utils/evm_helpers.py +7 -12
  36. wayfinder_paths/core/wallets/README.md +3 -6
  37. wayfinder_paths/run_strategy.py +62 -105
  38. wayfinder_paths/scripts/create_strategy.py +2 -27
  39. wayfinder_paths/scripts/make_wallets.py +1 -25
  40. wayfinder_paths/scripts/run_strategy.py +37 -9
  41. wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1 -3
  42. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +87 -138
  43. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +96 -58
  44. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +2 -17
  45. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +4 -1
  46. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +107 -29
  47. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +53 -14
  48. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +108 -0
  49. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +11 -0
  50. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2975 -0
  51. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +886 -0
  52. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -7
  53. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +2 -7
  54. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -4
  55. wayfinder_paths/templates/adapter/README.md +5 -21
  56. wayfinder_paths/templates/adapter/adapter.py +1 -2
  57. wayfinder_paths/templates/adapter/test_adapter.py +1 -1
  58. wayfinder_paths/templates/strategy/README.md +4 -21
  59. wayfinder_paths/templates/strategy/test_strategy.py +0 -4
  60. wayfinder_paths/tests/test_smoke_manifest.py +17 -2
  61. {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/METADATA +64 -201
  62. {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/RECORD +64 -71
  63. wayfinder_paths/adapters/balance_adapter/manifest.yaml +0 -8
  64. wayfinder_paths/adapters/brap_adapter/manifest.yaml +0 -11
  65. wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +0 -10
  66. wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +0 -8
  67. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +0 -11
  68. wayfinder_paths/adapters/pool_adapter/manifest.yaml +0 -10
  69. wayfinder_paths/adapters/token_adapter/manifest.yaml +0 -6
  70. wayfinder_paths/core/clients/SimulationClient.py +0 -192
  71. wayfinder_paths/core/clients/TransactionClient.py +0 -63
  72. wayfinder_paths/core/engine/manifest.py +0 -97
  73. wayfinder_paths/scripts/validate_manifests.py +0 -213
  74. wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +0 -23
  75. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +0 -7
  76. wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +0 -17
  77. wayfinder_paths/templates/adapter/manifest.yaml +0 -6
  78. wayfinder_paths/templates/strategy/manifest.yaml +0 -8
  79. {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/LICENSE +0 -0
  80. {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/WHEEL +0 -0
@@ -1,7 +1,6 @@
1
1
  # Stablecoin Yield Strategy
2
2
 
3
3
  - Entrypoint: `strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy`
4
- - Manifest: `manifest.yaml`
5
4
  - Examples: `examples.json`
6
5
  - Tests: `test_strategy.py`
7
6
 
@@ -87,10 +86,4 @@ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --ac
87
86
  poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --action update --config $(pwd)/config.json
88
87
  ```
89
88
 
90
- You can also load the manifest explicitly:
91
-
92
- ```bash
93
- poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml --action status --config $(pwd)/config.json
94
- ```
95
-
96
89
  Wallet addresses are auto-populated from `wallets.json` when you run `wayfinder_paths/scripts/make_wallets.py`. Set `NETWORK=testnet` in `config.json` to dry-run operations against mocked services.
@@ -74,7 +74,7 @@ class StablecoinYieldStrategy(Strategy):
74
74
  gas_threshold=GAS_MAXIMUM * GAS_SAFETY_FRACTION,
75
75
  # risk indicators
76
76
  volatility=Volatility.LOW,
77
- volatility_description_short=(
77
+ volatility_description=(
78
78
  "Capital sits in Base stablecoin lending pools, so price swings are minimal."
79
79
  ),
80
80
  directionality=Directionality.MARKET_NEUTRAL,
@@ -155,7 +155,6 @@ class StablecoinYieldStrategy(Strategy):
155
155
  *,
156
156
  main_wallet: dict[str, Any] | None = None,
157
157
  strategy_wallet: dict[str, Any] | None = None,
158
- simulation: bool = False,
159
158
  web3_service=None,
160
159
  api_key: str | None = None,
161
160
  ):
@@ -167,7 +166,6 @@ class StablecoinYieldStrategy(Strategy):
167
166
  merged_config["strategy_wallet"] = strategy_wallet
168
167
 
169
168
  self.config = merged_config
170
- self.simulation = simulation
171
169
  self.deposited_amount = 0
172
170
  self.current_pool = None
173
171
  self.current_apy = 0
@@ -198,7 +196,6 @@ class StablecoinYieldStrategy(Strategy):
198
196
  tx_adapter = LocalTokenTxnService(
199
197
  adapter_config,
200
198
  wallet_provider=wallet_provider,
201
- simulation=self.simulation,
202
199
  )
203
200
  web3_service = DefaultWeb3Service(
204
201
  wallet_provider=wallet_provider, evm_transactions=tx_adapter
@@ -210,9 +207,7 @@ class StablecoinYieldStrategy(Strategy):
210
207
  token_adapter = TokenAdapter()
211
208
  ledger_adapter = LedgerAdapter()
212
209
  pool_adapter = PoolAdapter()
213
- brap_adapter = BRAPAdapter(
214
- web3_service=web3_service, simulation=self.simulation
215
- )
210
+ brap_adapter = BRAPAdapter(web3_service=web3_service)
216
211
 
217
212
  self.register_adapters(
218
213
  [
@@ -46,7 +46,6 @@ def strategy():
46
46
  config=mock_config,
47
47
  main_wallet=mock_config["main_wallet"],
48
48
  strategy_wallet=mock_config["strategy_wallet"],
49
- simulation=True,
50
49
  )
51
50
 
52
51
  if hasattr(s, "balance_adapter") and s.balance_adapter:
@@ -130,9 +129,6 @@ def strategy():
130
129
  )
131
130
 
132
131
  if hasattr(s, "pool_adapter") and s.pool_adapter:
133
- s.pool_adapter.find_high_yield_pools = AsyncMock(
134
- return_value=(True, {"pools": [], "total_found": 0})
135
- )
136
132
  s.pool_adapter.get_pools_by_ids = AsyncMock(
137
133
  return_value=(
138
134
  True,
@@ -8,16 +8,15 @@ Adapters expose protocol-specific capabilities to strategies. They should be thi
8
8
  ```
9
9
  cp -r wayfinder_paths/templates/adapter wayfinder_paths/adapters/my_adapter
10
10
  ```
11
- 2. Rename `MyAdapter` in `adapter.py` and update `manifest.yaml` so the `entrypoint` matches (`adapters.my_adapter.adapter.MyAdapter`).
12
- 3. Declare the capabilities your adapter will provide and list any client dependencies (e.g., `PoolClient`, `LedgerClient`).
13
- 4. Implement the public methods that fulfill those capabilities.
11
+ 2. Rename `MyAdapter` in `adapter.py` to match your adapter's purpose.
12
+ 3. Implement the public methods that provide your adapter's capabilities.
13
+ 4. Add tests in `test_adapter.py`.
14
14
 
15
15
  ## Layout
16
16
 
17
17
  ```
18
18
  my_adapter/
19
19
  ├── adapter.py # Adapter implementation
20
- ├── manifest.yaml # Entrypoint + capabilities + dependency list
21
20
  ├── examples.json # Example payloads (optional but encouraged)
22
21
  ├── test_adapter.py # Pytest smoke tests
23
22
  └── README.md # Adapter-specific notes
@@ -57,21 +56,6 @@ class MyAdapter(BaseAdapter):
57
56
 
58
57
  Your adapter should return `(success, payload)` tuples for every operation, just like the built-in adapters do.
59
58
 
60
- ## Manifest
61
-
62
- Every adapter needs a manifest describing its import path, declared capabilities, and runtime dependencies.
63
-
64
- ```yaml
65
- schema_version: "0.1"
66
- entrypoint: "adapters.my_adapter.adapter.MyAdapter"
67
- capabilities:
68
- - "pool.read"
69
- dependencies:
70
- - "PoolClient"
71
- ```
72
-
73
- The `dependencies` list is informational today but helps reviewers understand which core clients you rely on.
74
-
75
59
  ## Testing
76
60
 
77
61
  `test_adapter.py` should cover the public methods you expose. Patch out remote clients with `unittest.mock.AsyncMock` so tests run offline.
@@ -99,7 +83,7 @@ async def test_get_pools():
99
83
 
100
84
  ## Best practices
101
85
 
102
- - Capabilities listed in `manifest.yaml` must correspond to methods you implement.
103
86
  - Keep adapters stateless and idempotent—strategies may reuse instances across operations.
104
87
  - Use `self.logger` for contextual logging (BaseAdapter has already bound the adapter name).
105
- - Raise `NotImplementedError` for manifest capabilities you intentionally do not support yet.
88
+ - Return `(success, payload)` tuples consistently for all operations.
89
+ - Raise `NotImplementedError` for capabilities you intentionally do not support yet.
@@ -6,8 +6,7 @@ from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
6
6
  class MyAdapter(BaseAdapter):
7
7
  """
8
8
  Template adapter for a protocol/exchange integration.
9
- Copy this folder, rename it (e.g., my_adapter), update manifest entrypoint,
10
- and implement the capabilities your manifest declares.
9
+ Copy this folder, rename it (e.g., my_adapter), and implement your adapter methods.
11
10
  """
12
11
 
13
12
  adapter_type: str = "MY_ADAPTER"
@@ -40,7 +40,7 @@ class TestMyAdapter:
40
40
  assert isinstance(ok, bool)
41
41
 
42
42
  def test_capabilities(self, adapter):
43
- """Test adapter capabilities match manifest"""
43
+ """Test adapter capabilities"""
44
44
  assert hasattr(adapter, "adapter_type")
45
45
 
46
46
  @pytest.mark.asyncio
@@ -1,6 +1,6 @@
1
1
  # Strategy Template
2
2
 
3
- This template provides the scaffolding for a new strategy. It mirrors the structure in `wayfinder_paths/strategies/...` and wires into the runner via a manifest.
3
+ This template provides the scaffolding for a new strategy. It mirrors the structure in `wayfinder_paths/strategies/...`.
4
4
 
5
5
  ## Quick Start
6
6
 
@@ -8,7 +8,7 @@ This template provides the scaffolding for a new strategy. It mirrors the struct
8
8
  ```
9
9
  cp -r wayfinder_paths/templates/strategy wayfinder_paths/strategies/my_strategy
10
10
  ```
11
- 2. Rename the class in `strategy.py` and update `manifest.yaml` so `entrypoint` points to your new module (for example `strategies.my_strategy.strategy.MyStrategy`).
11
+ 2. Rename the class in `strategy.py` to match your strategy name.
12
12
  3. Fill out `examples.json` with sample CLI invocations and `test_strategy.py` with at least one smoke test.
13
13
  4. Implement the required strategy methods (`deposit`, `update`, `_status`, optionally override `withdraw`).
14
14
 
@@ -17,7 +17,6 @@ This template provides the scaffolding for a new strategy. It mirrors the struct
17
17
  ```
18
18
  my_strategy/
19
19
  ├── strategy.py # Strategy implementation
20
- ├── manifest.yaml # Entrypoint + adapter requirements + policy
21
20
  ├── examples.json # Example CLI payloads
22
21
  ├── test_strategy.py # Pytest-based smoke tests
23
22
  └── README.md # Strategy-specific documentation
@@ -98,22 +97,6 @@ class MyStrategy(Strategy):
98
97
  }
99
98
  ```
100
99
 
101
- ## Manifest configuration
102
-
103
- Your `manifest.yaml` should define:
104
-
105
- ```yaml
106
- schema_version: "0.1"
107
- entrypoint: "strategies.my_strategy.strategy.MyStrategy"
108
- permissions:
109
- policy: "(wallet.id == 'FORMAT_WALLET_ID') && (eth.tx.to == '0x...')"
110
- adapters:
111
- - name: "BALANCE"
112
- capabilities: ["wallet_read", "wallet_transfer"]
113
- - name: "POOL"
114
- capabilities: ["pool.read", "pool.analytics"]
115
- ```
116
-
117
100
  ## Testing
118
101
 
119
102
  `test_strategy.py` should cover at least deposit/update/status. Use `pytest.mark.asyncio` and patch adapters or services as needed.
@@ -149,5 +132,5 @@ poetry run python wayfinder_paths/run_strategy.py my_strategy --action status --
149
132
 
150
133
  - Return `(success: bool, message: str)` tuples from `deposit`/`update`.
151
134
  - Always populate `portfolio_value`, `net_deposit`, and `strategy_status` keys in `_status`.
152
- - Use the adapters declared in `manifest.yaml`—they are injected via `register_adapters`.
153
- - Keep on-chain permissions tight by scoping the manifest policy to the strategy wallet and expected contracts.
135
+ - Register adapters via `register_adapters` in your `__init__` method.
136
+ - Keep strategy logic clear and well-documented.
@@ -58,7 +58,6 @@ def strategy():
58
58
  config=mock_config,
59
59
  main_wallet=mock_config["main_wallet"],
60
60
  strategy_wallet=mock_config["strategy_wallet"],
61
- simulation=True,
62
61
  )
63
62
 
64
63
  # TODO: Add mocking for your adapters here if needed
@@ -77,9 +76,6 @@ def strategy():
77
76
  # s.balance_adapter.get_token_balance_for_wallet = AsyncMock(
78
77
  # side_effect=get_balance_side_effect
79
78
  # )
80
- # s.balance_adapter.get_all_enriched_token_balances_for_wallet = AsyncMock(
81
- # return_value=(True, {"balances": []})
82
- # )
83
79
 
84
80
  # Example for token_adapter:
85
81
  # if hasattr(s, "token_adapter") and s.token_adapter:
@@ -14,6 +14,16 @@ class FakeAdapter(BaseAdapter):
14
14
  return {"asset": asset, "amount": 100}
15
15
 
16
16
 
17
+ class FakeLedgerAdapter(BaseAdapter):
18
+ adapter_type = "LEDGER"
19
+
20
+ async def connect(self) -> bool:
21
+ return True
22
+
23
+ async def record_strategy_snapshot(self, **kwargs):
24
+ pass
25
+
26
+
17
27
  class FakeStrategy(Strategy):
18
28
  name = "Fake Strategy"
19
29
 
@@ -26,7 +36,7 @@ class FakeStrategy(Strategy):
26
36
  async def withdraw(self, amount: float = 0) -> StatusTuple:
27
37
  return (True, "withdrew")
28
38
 
29
- async def status(self) -> StatusDict:
39
+ async def _status(self) -> StatusDict:
30
40
  return {"total_earned": 0.0, "strategy_status": {"ok": True}}
31
41
 
32
42
  @staticmethod
@@ -36,8 +46,13 @@ class FakeStrategy(Strategy):
36
46
 
37
47
  @pytest.mark.asyncio
38
48
  async def test_smoke_deposit_update_withdraw_status():
39
- s = FakeStrategy()
49
+ s = FakeStrategy(
50
+ config={
51
+ "strategy_wallet": {"address": "0x1234567890123456789012345678901234567890"}
52
+ }
53
+ )
40
54
  s.register_adapters([FakeAdapter("fake")])
55
+ s.ledger_adapter = FakeLedgerAdapter("ledger")
41
56
  ok, msg = await s.deposit(amount=1)
42
57
  assert ok
43
58
  ok, msg = await s.update()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wayfinder-paths
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Wayfinder Path: strategies and adapters
5
5
  Author: Wayfinder
6
6
  Author-email: dev@wayfinder.ai
@@ -47,6 +47,9 @@ poetry install
47
47
  just create-wallets
48
48
  # Or manually: poetry run python wayfinder_paths/scripts/make_wallets.py -n 1
49
49
 
50
+ # To test a specific strategy
51
+ just just create-wallet stablecoin_yield_strategy
52
+
50
53
  # Copy and configure
51
54
  cp wayfinder_paths/config.example.json config.json
52
55
  # Edit config.json with your Wayfinder credentials
@@ -54,9 +57,6 @@ cp wayfinder_paths/config.example.json config.json
54
57
  # Run a strategy locally (one-shot status check)
55
58
  poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --action status --config config.json
56
59
 
57
- export WAYFINDER_API_KEY="sk_live_abc123..."
58
- poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --config config.json
59
-
60
60
  # Run continuously (production mode)
61
61
  poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --config config.json
62
62
  ```
@@ -75,7 +75,6 @@ wayfinder_paths/
75
75
  │ ├── adapters/ # Your exchange/protocol integrations (community contributions)
76
76
  │ │ ├── balance_adapter/
77
77
  │ │ │ ├── adapter.py # Adapter implementation
78
- │ │ │ ├── manifest.yaml # Adapter manifest (caps, entrypoint)
79
78
  │ │ │ ├── examples.json # Example inputs for smoke
80
79
  │ │ │ ├── README.md # Local notes
81
80
  │ │ │ └── test_adapter.py # Local smoke test
@@ -84,7 +83,6 @@ wayfinder_paths/
84
83
  │ ├── strategies/ # Your trading strategies (community contributions)
85
84
  │ │ ├── stablecoin_yield_strategy/
86
85
  │ │ │ ├── strategy.py # Strategy implementation
87
- │ │ │ ├── manifest.yaml # Strategy manifest
88
86
  │ │ │ ├── examples.json # Example inputs
89
87
  │ │ │ ├── README.md # Local notes
90
88
  │ │ │ └── test_strategy.py # Local smoke test
@@ -111,10 +109,9 @@ We welcome contributions! This is an open-source project where community members
111
109
  3. **Copy a template** to get started:
112
110
  - **For adapters**: Copy `wayfinder_paths/templates/adapter/` to `wayfinder_paths/adapters/my_adapter/`
113
111
  - **For strategies**: Copy `wayfinder_paths/templates/strategy/` to `wayfinder_paths/strategies/my_strategy/`
114
- 4. **Customize** the template (rename classes, update manifest, implement methods)
112
+ 4. **Customize** the template (rename classes, implement methods)
115
113
  5. **Test your code** thoroughly using the provided test framework
116
- 6. **Validate manifests**: Run `just validate-manifests`
117
- 7. **Submit a Pull Request** with a clear description of your changes
114
+ 6. **Submit a Pull Request** with a clear description of your changes
118
115
 
119
116
  ### What You Can Contribute
120
117
 
@@ -128,29 +125,18 @@ We welcome contributions! This is an open-source project where community members
128
125
 
129
126
  - **Start from the template**: Copy `wayfinder_paths/templates/adapter/` as a starting point
130
127
  - Extend `BaseAdapter` from `wayfinder_paths/core/adapters/BaseAdapter.py`
131
- - Create a `manifest.yaml` (template at `wayfinder_paths/templates/adapter/manifest.yaml`) with:
132
- - `entrypoint`: Full import path to your adapter class
133
- - `capabilities`: List of capabilities your adapter provides
134
- - `dependencies`: List of required client classes (e.g., `PoolClient`, `TokenClient`)
135
- - Implement methods that fulfill the declared capabilities
128
+ - Implement your adapter methods
136
129
  - Add comprehensive tests in `test_adapter.py`
137
130
  - Include usage examples in `examples.json`
138
131
  - Document your adapter in `README.md`
139
- - Validate your manifest: `just validate-manifests`
140
132
 
141
133
  #### For Strategies
142
134
 
143
135
  - **Start from the template**: Use `just create-strategy "Strategy Name"` to create a new strategy with its own wallet, or copy `wayfinder_paths/templates/strategy/` manually
144
136
  - Extend `Strategy` from `wayfinder_paths/core/strategies/Strategy.py`
145
- - Create a `manifest.yaml` (template at `wayfinder_paths/templates/strategy/manifest.yaml`) with:
146
- - `entrypoint`: Full import path to your strategy class
147
- - `name`: Strategy directory name (used for wallet lookup)
148
- - `permissions.policy`: Security policy for transaction permissions
149
- - `adapters`: List of required adapters and their capabilities
150
137
  - Implement required methods: `deposit()`, `update()`, `status()`, `withdraw()`
151
138
  - Include test cases in `test_strategy.py`
152
139
  - Add example configurations in `examples.json`
153
- - Validate your manifest: `just validate-manifests`
154
140
 
155
141
  #### General Guidelines
156
142
 
@@ -186,10 +172,7 @@ cp -r wayfinder_paths/templates/strategy wayfinder_paths/strategies/my_strategy
186
172
 
187
173
  # 5. Customize the template (see template README.md files for details)
188
174
 
189
- # 6. Validate your manifest
190
- just validate-manifests
191
-
192
- # 7. Run tests
175
+ # 6. Run tests
193
176
  poetry run pytest -k smoke -v
194
177
 
195
178
  # Or test your specific contribution
@@ -213,8 +196,8 @@ The platform uses a unified client system for all API interactions. Clients are
213
196
 
214
197
  ### Clients vs Adapters
215
198
 
216
- - **Clients**: Low-level, reusable service wrappers that talk to networks and external APIs. They handle auth, headers, retries, and response parsing, and expose generic capabilities (e.g., token info, tx building). Examples: `TokenClient`, `TransactionClient`, `WalletClient`.
217
- - **Adapters**: Strategy-facing integrations for a specific exchange/protocol. They compose one or more clients to implement a manifest of capabilities (e.g., `supply`, `borrow`, `place_order`). Adapters encapsulate protocol-specific semantics and raise `NotImplementedError` for unsupported ops.
199
+ - **Clients**: Low-level, reusable service wrappers that talk to networks and external APIs. They handle auth, headers, retries, and response parsing, and expose generic capabilities (e.g., token info, tx building). Examples: `TokenClient`, `WalletClient`.
200
+ - **Adapters**: Strategy-facing integrations for a specific exchange/protocol. They compose one or more clients to implement a set of capabilities (e.g., `supply`, `borrow`, `place_order`). Adapters encapsulate protocol-specific semantics and raise `NotImplementedError` for unsupported ops.
218
201
 
219
202
  Recommended usage:
220
203
 
@@ -224,168 +207,6 @@ Recommended usage:
224
207
 
225
208
  Data flow: `Strategy` → `Adapter` → `Client(s)` → network/API.
226
209
 
227
- ### Manifests
228
-
229
- Every adapter and strategy requires a `manifest.yaml` file that declares its metadata, capabilities, and dependencies. Manifests are validated automatically in CI/CD and serve as the **single source of truth** for what each component can do.
230
-
231
- #### Adapter Manifests
232
-
233
- Adapter manifests declare the capabilities an adapter provides and the clients it depends on.
234
-
235
- **Template:** Copy `wayfinder_paths/templates/adapter/manifest.yaml` as a starting point.
236
-
237
- **Schema:**
238
-
239
- ```yaml
240
- schema_version: "0.1"
241
- entrypoint: "adapters.my_adapter.adapter.MyAdapter"
242
- capabilities:
243
- - "pool.read"
244
- - "pool.analytics"
245
- dependencies:
246
- - "PoolClient"
247
- - "TokenClient"
248
- ```
249
-
250
- **Fields:**
251
-
252
- - `schema_version`: Manifest schema version (currently `"0.1"`)
253
- - `entrypoint`: Full Python import path to the adapter class (required)
254
- - `capabilities`: List of abstract capabilities this adapter provides (required, non-empty)
255
- - `dependencies`: List of client class names from `core.clients` that this adapter requires (required, non-empty)
256
-
257
- **Example** (`wayfinder_paths/adapters/pool_adapter/manifest.yaml`):
258
-
259
- ```yaml
260
- schema_version: "0.1"
261
- entrypoint: "adapters.pool_adapter.adapter.PoolAdapter"
262
- capabilities:
263
- - "pool.read"
264
- - "pool.analytics"
265
- - "pool.discovery"
266
- - "llama.data"
267
- - "pool.reports"
268
- dependencies:
269
- - "PoolClient"
270
- ```
271
-
272
- #### Strategy Manifests
273
-
274
- Strategy manifests declare permissions and required adapters with their capabilities.
275
-
276
- **Template:** Copy `wayfinder_paths/templates/strategy/manifest.yaml` as a starting point.
277
-
278
- **Schema:**
279
-
280
- ```yaml
281
- schema_version: "0.1"
282
- entrypoint: "strategies.my_strategy.strategy.MyStrategy"
283
- permissions:
284
- policy: "(wallet.id == 'FORMAT_WALLET_ID') && (eth.tx.to == '0x...')"
285
- adapters:
286
- - name: "POOL"
287
- capabilities: ["pool.read", "pool.analytics"]
288
- - name: "BRAP"
289
- capabilities: ["swap.quote", "swap.execute"]
290
- ```
291
-
292
- **Fields:**
293
-
294
- - `schema_version`: Manifest schema version (currently `"0.1"`)
295
- - `entrypoint`: Full Python import path to the strategy class (required)
296
- - `name`: Strategy directory name (optional, used for wallet lookup - defaults to directory name)
297
- - `permissions.policy`: Security policy string that defines transaction permissions (required, non-empty)
298
- - `adapters`: List of required adapters with their names and needed capabilities (required, non-empty)
299
- - `name`: Adapter type identifier (e.g., "POOL", "BRAP")
300
- - `capabilities`: List of capabilities required from this adapter
301
-
302
- **Example** (`wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml`):
303
-
304
- ```yaml
305
- schema_version: "0.1"
306
- entrypoint: "strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy"
307
- permissions:
308
- policy: "(wallet.id == 'FORMAT_WALLET_ID') && ((eth.tx.data[0..10] == '0x095ea7b3' && eth.tx.data[34..74] == 'f75584ef6673ad213a685a1b58cc0330b8ea22cf') || (eth.tx.to == '0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf'))"
309
- adapters:
310
- - name: "BALANCE"
311
- capabilities: ["wallet_read", "wallet_transfer"]
312
- - name: "POOL"
313
- capabilities: ["pool.read", "pool.analytics"]
314
- - name: "BRAP"
315
- capabilities: ["swap.quote", "swap.execute"]
316
- ```
317
-
318
- #### Manifest Validation
319
-
320
- Manifests are automatically validated to ensure:
321
-
322
- - Schema compliance (all required fields present, correct types)
323
- - Entrypoint classes exist and are importable
324
- - Dependencies are valid client classes
325
- - Permissions policies are non-empty
326
-
327
- **Validate locally:**
328
-
329
- ```bash
330
- # Validate all manifests
331
- just validate-manifests
332
-
333
- # Or manually
334
- PYTHONPATH=wayfinder_paths poetry run python wayfinder_paths/scripts/validate_manifests.py
335
- ```
336
-
337
- Validation runs automatically in CI/CD on every PR and push to main. All manifests must be valid before merging.
338
-
339
- **How Validation Works:**
340
-
341
- The `validate_manifests.py` script performs multi-stage validation:
342
-
343
- 1. **Schema Validation** (via Pydantic models):
344
- - Loads YAML file and validates against `AdapterManifest` or `StrategyManifest` schema
345
- - Checks required fields, types, and basic constraints (e.g., capabilities cannot be empty)
346
- - Validates entrypoint format (must be full import path like `"adapters.pool_adapter.adapter.PoolAdapter"`)
347
-
348
- 2. **Entrypoint Verification**:
349
- - **For Adapters**: Imports the entrypoint class and verifies it's a subclass of `BaseAdapter`
350
- - **For Strategies**: Imports the entrypoint class and verifies it's a subclass of `Strategy`
351
- - Uses Python's `__import__()` to dynamically import the module and class
352
- - Catches import errors, missing classes, and type mismatches
353
-
354
- 3. **Dependency Verification** (adapters only):
355
- - Validates that all declared dependencies (e.g., `PoolClient`, `TokenClient`) exist in `core.clients`
356
- - Attempts to import each dependency as `core.clients.{DepName}`
357
-
358
- 4. **Permissions Validation** (strategies only):
359
- - Validated by Pydantic: ensures `permissions.policy` exists and is non-empty
360
- - Policy syntax is not parsed/validated (assumed to be valid at runtime)
361
-
362
- **Validation Flow:**
363
-
364
- ```
365
- For each manifest file:
366
- 1. Load YAML → Parse with Pydantic (schema validation)
367
- 2. Import entrypoint class → Verify inheritance (entrypoint validation)
368
- 3. For adapters: Import dependencies → Verify they exist (dependency validation)
369
- 4. Collect all errors → Report results
370
- ```
371
-
372
- The script automatically discovers all manifests by scanning:
373
-
374
- - `wayfinder_paths/adapters/*/manifest.yaml` for adapter manifests
375
- - `wayfinder_paths/strategies/*/manifest.yaml` for strategy manifests
376
-
377
- All errors are collected and reported at the end, with the script exiting with code 1 if any validation fails.
378
-
379
- #### Capabilities
380
-
381
- Capabilities are abstract operation identifiers (e.g., `"pool.read"`, `"swap.execute"`) declared in manifests. They represent what operations an adapter can perform, not specific method names. The manifest is the **single source of truth** for capabilities—they are not duplicated in code.
382
-
383
- When creating an adapter:
384
-
385
- 1. Declare capabilities in your `manifest.yaml`
386
- 2. Implement methods that fulfill those capabilities
387
- 3. Capabilities are validated at manifest validation time (entrypoint must be importable)
388
-
389
210
  ### Configuration
390
211
 
391
212
  Configuration is split between:
@@ -415,14 +236,7 @@ strategy = StablecoinYieldStrategy(
415
236
  )
416
237
  ```
417
238
 
418
- **Option B: Set Environment Variable**
419
-
420
- ```bash
421
- export WAYFINDER_API_KEY="sk_live_abc123..."
422
- # All clients will automatically discover and use this
423
- ```
424
-
425
- **Option C: Add to config.json**
239
+ **Option B: Add to config.json**
426
240
 
427
241
  ```json
428
242
  {
@@ -435,7 +249,7 @@ export WAYFINDER_API_KEY="sk_live_abc123..."
435
249
  }
436
250
  ```
437
251
 
438
- **Priority Order:** Constructor parameter > `config.json` (user.api_key or system.api_key) > `WAYFINDER_API_KEY` environment variable
252
+ **Priority Order:** Constructor parameter > `config.json` (user.api_key or system.api_key)
439
253
 
440
254
  **Note:** API keys in `config.json` are loaded directly by `WayfinderClient` via `_load_config_credentials()`, not through the `UserConfig` or `SystemConfig` dataclasses. This allows flexible credential loading.
441
255
 
@@ -483,7 +297,7 @@ class MyAdapter(BaseAdapter):
483
297
  self.pool_client = PoolClient()
484
298
 
485
299
  async def connect(self) -> bool:
486
- """No-op for read-only adapters, but kept for manifest compatibility."""
300
+ """No-op for read-only adapters, but kept for interface consistency."""
487
301
  return True
488
302
 
489
303
  async def get_pools(self, pool_ids: list[str]):
@@ -516,7 +330,7 @@ class MyStrategy(Strategy):
516
330
  super().__init__(api_key=api_key) # Pass to base class for auto-discovery
517
331
  self.config = config or {}
518
332
  web3_service = DefaultWeb3Service(self.config)
519
- # Adapters automatically discover API key from env var (set by Strategy.__init__)
333
+ # Adapters automatically discover API key from constructor or config.json
520
334
  balance_adapter = BalanceAdapter(self.config, web3_service=web3_service)
521
335
  self.register_adapters([balance_adapter])
522
336
  self.balance_adapter = balance_adapter
@@ -556,6 +370,54 @@ class MyStrategy(Strategy):
556
370
  }
557
371
  ```
558
372
 
373
+ ### Built-in Strategies
374
+
375
+ The following strategies are available and can be run using the CLI:
376
+
377
+ | Strategy | Description | Chain |
378
+ |----------|-------------|-------|
379
+ | `basis_trading_strategy` | Delta-neutral basis trading | - |
380
+ | `hyperlend_stable_yield_strategy` | Stable yield on HyperLend | HyperEVM |
381
+ | `moonwell_wsteth_loop_strategy` | Leveraged wstETH yield loop | Base |
382
+
383
+ #### Running Strategies
384
+
385
+ ```bash
386
+ # Check strategy status
387
+ poetry run python wayfinder_paths/scripts/run_strategy.py \
388
+ --strategy moonwell_wsteth_loop_strategy \
389
+ --main-wallet-label main \
390
+ --strategy-wallet-label moonwell_wsteth_loop_strategy \
391
+ status
392
+
393
+ # Deposit funds (USDC amount, optional ETH for gas)
394
+ poetry run python wayfinder_paths/scripts/run_strategy.py \
395
+ --strategy moonwell_wsteth_loop_strategy \
396
+ --main-wallet-label main \
397
+ --strategy-wallet-label moonwell_wsteth_loop_strategy \
398
+ deposit --usdc 100 --eth 0.01
399
+
400
+ # Run periodic update
401
+ poetry run python wayfinder_paths/scripts/run_strategy.py \
402
+ --strategy moonwell_wsteth_loop_strategy \
403
+ --main-wallet-label main \
404
+ --strategy-wallet-label moonwell_wsteth_loop_strategy \
405
+ update
406
+
407
+ # Withdraw funds (omit --amount for full withdrawal)
408
+ poetry run python wayfinder_paths/scripts/run_strategy.py \
409
+ --strategy moonwell_wsteth_loop_strategy \
410
+ --main-wallet-label main \
411
+ --strategy-wallet-label moonwell_wsteth_loop_strategy \
412
+ withdraw --amount 50
413
+
414
+ # Run in simulation mode (no real transactions)
415
+ poetry run python wayfinder_paths/scripts/run_strategy.py \
416
+ --strategy moonwell_wsteth_loop_strategy \
417
+ --simulation \
418
+ status
419
+ ```
420
+
559
421
  ### Built-in adapters
560
422
 
561
423
  - **BALANCE (BalanceAdapter)**: wraps `WalletClient`/`TokenClient` to read wallet, token, and pool balances and now orchestrates transfers between the main/strategy wallets with ledger bookkeeping. Requires a `Web3Service` so it can share the same wallet provider as the strategy.
@@ -564,8 +426,9 @@ class MyStrategy(Strategy):
564
426
  - **LEDGER (LedgerAdapter)**: records deposits, withdrawals, custom operations, and cashflows via `LedgerClient`, and can read strategy transaction summaries.
565
427
  - **TOKEN (TokenAdapter)**: lightweight wrapper around `TokenClient` for token metadata, live price snapshots, and gas token lookups.
566
428
  - **HYPERLEND (HyperlendAdapter)**: connects to `HyperlendClient` for lending/supply caps inside the HyperLend strategy.
429
+ - **MOONWELL (MoonwellAdapter)**: interfaces with Moonwell protocol on Base for lending, borrowing, collateral management, and WELL rewards.
567
430
 
568
- Each strategy manifest declares which adapters it needs and which capabilities it consumes. Adapters must implement the behavior promised in their manifest (or raise `NotImplementedError` if invoked outside the manifest contract).
431
+ Strategies register the adapters they need in their `__init__` method. Adapters implement their specific capabilities and raise `NotImplementedError` for unsupported operations.
569
432
 
570
433
  ## 🧪 Testing
571
434