wayfinder-paths 0.1.9__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.
- wayfinder_paths/CONFIG_GUIDE.md +1 -1
- wayfinder_paths/adapters/balance_adapter/README.md +1 -2
- wayfinder_paths/adapters/balance_adapter/adapter.py +4 -4
- wayfinder_paths/adapters/brap_adapter/adapter.py +139 -23
- wayfinder_paths/adapters/moonwell_adapter/README.md +174 -0
- wayfinder_paths/adapters/moonwell_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +1226 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +635 -0
- wayfinder_paths/core/clients/AuthClient.py +3 -0
- wayfinder_paths/core/clients/WayfinderClient.py +2 -2
- wayfinder_paths/core/constants/__init__.py +0 -2
- wayfinder_paths/core/constants/base.py +6 -2
- wayfinder_paths/core/constants/moonwell_abi.py +411 -0
- wayfinder_paths/core/engine/StrategyJob.py +3 -0
- wayfinder_paths/core/services/base.py +55 -0
- wayfinder_paths/core/services/local_evm_txn.py +288 -208
- wayfinder_paths/core/services/local_token_txn.py +46 -26
- wayfinder_paths/core/strategies/descriptors.py +1 -1
- wayfinder_paths/run_strategy.py +34 -74
- wayfinder_paths/scripts/create_strategy.py +2 -27
- wayfinder_paths/scripts/run_strategy.py +37 -7
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +1 -1
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -15
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +1 -1
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +108 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +11 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2975 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +886 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +1 -1
- wayfinder_paths/templates/adapter/README.md +5 -21
- wayfinder_paths/templates/adapter/adapter.py +1 -2
- wayfinder_paths/templates/adapter/test_adapter.py +1 -1
- wayfinder_paths/templates/strategy/README.md +4 -21
- wayfinder_paths/tests/test_smoke_manifest.py +17 -2
- {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.10.dist-info}/METADATA +60 -187
- {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.10.dist-info}/RECORD +39 -44
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +0 -8
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +0 -11
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +0 -10
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +0 -8
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +0 -11
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +0 -10
- wayfinder_paths/adapters/token_adapter/manifest.yaml +0 -6
- wayfinder_paths/core/engine/manifest.py +0 -97
- wayfinder_paths/scripts/validate_manifests.py +0 -213
- wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +0 -23
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +0 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +0 -17
- wayfinder_paths/templates/adapter/manifest.yaml +0 -6
- wayfinder_paths/templates/strategy/manifest.yaml +0 -8
- {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.10.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.9.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
|
-
|
|
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,
|
|
@@ -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`
|
|
12
|
-
3.
|
|
13
|
-
4.
|
|
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
|
-
-
|
|
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),
|
|
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"
|
|
@@ -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
|
|
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`
|
|
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
|
-
-
|
|
153
|
-
- Keep
|
|
135
|
+
- Register adapters via `register_adapters` in your `__init__` method.
|
|
136
|
+
- Keep strategy logic clear and well-documented.
|
|
@@ -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
|
|
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.
|
|
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
|
|
@@ -72,7 +75,6 @@ wayfinder_paths/
|
|
|
72
75
|
│ ├── adapters/ # Your exchange/protocol integrations (community contributions)
|
|
73
76
|
│ │ ├── balance_adapter/
|
|
74
77
|
│ │ │ ├── adapter.py # Adapter implementation
|
|
75
|
-
│ │ │ ├── manifest.yaml # Adapter manifest (caps, entrypoint)
|
|
76
78
|
│ │ │ ├── examples.json # Example inputs for smoke
|
|
77
79
|
│ │ │ ├── README.md # Local notes
|
|
78
80
|
│ │ │ └── test_adapter.py # Local smoke test
|
|
@@ -81,7 +83,6 @@ wayfinder_paths/
|
|
|
81
83
|
│ ├── strategies/ # Your trading strategies (community contributions)
|
|
82
84
|
│ │ ├── stablecoin_yield_strategy/
|
|
83
85
|
│ │ │ ├── strategy.py # Strategy implementation
|
|
84
|
-
│ │ │ ├── manifest.yaml # Strategy manifest
|
|
85
86
|
│ │ │ ├── examples.json # Example inputs
|
|
86
87
|
│ │ │ ├── README.md # Local notes
|
|
87
88
|
│ │ │ └── test_strategy.py # Local smoke test
|
|
@@ -108,10 +109,9 @@ We welcome contributions! This is an open-source project where community members
|
|
|
108
109
|
3. **Copy a template** to get started:
|
|
109
110
|
- **For adapters**: Copy `wayfinder_paths/templates/adapter/` to `wayfinder_paths/adapters/my_adapter/`
|
|
110
111
|
- **For strategies**: Copy `wayfinder_paths/templates/strategy/` to `wayfinder_paths/strategies/my_strategy/`
|
|
111
|
-
4. **Customize** the template (rename classes,
|
|
112
|
+
4. **Customize** the template (rename classes, implement methods)
|
|
112
113
|
5. **Test your code** thoroughly using the provided test framework
|
|
113
|
-
6. **
|
|
114
|
-
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
|
|
115
115
|
|
|
116
116
|
### What You Can Contribute
|
|
117
117
|
|
|
@@ -125,29 +125,18 @@ We welcome contributions! This is an open-source project where community members
|
|
|
125
125
|
|
|
126
126
|
- **Start from the template**: Copy `wayfinder_paths/templates/adapter/` as a starting point
|
|
127
127
|
- Extend `BaseAdapter` from `wayfinder_paths/core/adapters/BaseAdapter.py`
|
|
128
|
-
-
|
|
129
|
-
- `entrypoint`: Full import path to your adapter class
|
|
130
|
-
- `capabilities`: List of capabilities your adapter provides
|
|
131
|
-
- `dependencies`: List of required client classes (e.g., `PoolClient`, `TokenClient`)
|
|
132
|
-
- Implement methods that fulfill the declared capabilities
|
|
128
|
+
- Implement your adapter methods
|
|
133
129
|
- Add comprehensive tests in `test_adapter.py`
|
|
134
130
|
- Include usage examples in `examples.json`
|
|
135
131
|
- Document your adapter in `README.md`
|
|
136
|
-
- Validate your manifest: `just validate-manifests`
|
|
137
132
|
|
|
138
133
|
#### For Strategies
|
|
139
134
|
|
|
140
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
|
|
141
136
|
- Extend `Strategy` from `wayfinder_paths/core/strategies/Strategy.py`
|
|
142
|
-
- Create a `manifest.yaml` (template at `wayfinder_paths/templates/strategy/manifest.yaml`) with:
|
|
143
|
-
- `entrypoint`: Full import path to your strategy class
|
|
144
|
-
- `name`: Strategy directory name (used for wallet lookup)
|
|
145
|
-
- `permissions.policy`: Security policy for transaction permissions
|
|
146
|
-
- `adapters`: List of required adapters and their capabilities
|
|
147
137
|
- Implement required methods: `deposit()`, `update()`, `status()`, `withdraw()`
|
|
148
138
|
- Include test cases in `test_strategy.py`
|
|
149
139
|
- Add example configurations in `examples.json`
|
|
150
|
-
- Validate your manifest: `just validate-manifests`
|
|
151
140
|
|
|
152
141
|
#### General Guidelines
|
|
153
142
|
|
|
@@ -183,10 +172,7 @@ cp -r wayfinder_paths/templates/strategy wayfinder_paths/strategies/my_strategy
|
|
|
183
172
|
|
|
184
173
|
# 5. Customize the template (see template README.md files for details)
|
|
185
174
|
|
|
186
|
-
# 6.
|
|
187
|
-
just validate-manifests
|
|
188
|
-
|
|
189
|
-
# 7. Run tests
|
|
175
|
+
# 6. Run tests
|
|
190
176
|
poetry run pytest -k smoke -v
|
|
191
177
|
|
|
192
178
|
# Or test your specific contribution
|
|
@@ -211,7 +197,7 @@ The platform uses a unified client system for all API interactions. Clients are
|
|
|
211
197
|
### Clients vs Adapters
|
|
212
198
|
|
|
213
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`.
|
|
214
|
-
- **Adapters**: Strategy-facing integrations for a specific exchange/protocol. They compose one or more clients to implement a
|
|
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.
|
|
215
201
|
|
|
216
202
|
Recommended usage:
|
|
217
203
|
|
|
@@ -221,168 +207,6 @@ Recommended usage:
|
|
|
221
207
|
|
|
222
208
|
Data flow: `Strategy` → `Adapter` → `Client(s)` → network/API.
|
|
223
209
|
|
|
224
|
-
### Manifests
|
|
225
|
-
|
|
226
|
-
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.
|
|
227
|
-
|
|
228
|
-
#### Adapter Manifests
|
|
229
|
-
|
|
230
|
-
Adapter manifests declare the capabilities an adapter provides and the clients it depends on.
|
|
231
|
-
|
|
232
|
-
**Template:** Copy `wayfinder_paths/templates/adapter/manifest.yaml` as a starting point.
|
|
233
|
-
|
|
234
|
-
**Schema:**
|
|
235
|
-
|
|
236
|
-
```yaml
|
|
237
|
-
schema_version: "0.1"
|
|
238
|
-
entrypoint: "adapters.my_adapter.adapter.MyAdapter"
|
|
239
|
-
capabilities:
|
|
240
|
-
- "pool.read"
|
|
241
|
-
- "pool.analytics"
|
|
242
|
-
dependencies:
|
|
243
|
-
- "PoolClient"
|
|
244
|
-
- "TokenClient"
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
**Fields:**
|
|
248
|
-
|
|
249
|
-
- `schema_version`: Manifest schema version (currently `"0.1"`)
|
|
250
|
-
- `entrypoint`: Full Python import path to the adapter class (required)
|
|
251
|
-
- `capabilities`: List of abstract capabilities this adapter provides (required, non-empty)
|
|
252
|
-
- `dependencies`: List of client class names from `core.clients` that this adapter requires (required, non-empty)
|
|
253
|
-
|
|
254
|
-
**Example** (`wayfinder_paths/adapters/pool_adapter/manifest.yaml`):
|
|
255
|
-
|
|
256
|
-
```yaml
|
|
257
|
-
schema_version: "0.1"
|
|
258
|
-
entrypoint: "adapters.pool_adapter.adapter.PoolAdapter"
|
|
259
|
-
capabilities:
|
|
260
|
-
- "pool.read"
|
|
261
|
-
- "pool.analytics"
|
|
262
|
-
- "pool.discovery"
|
|
263
|
-
- "llama.data"
|
|
264
|
-
- "pool.reports"
|
|
265
|
-
dependencies:
|
|
266
|
-
- "PoolClient"
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
#### Strategy Manifests
|
|
270
|
-
|
|
271
|
-
Strategy manifests declare permissions and required adapters with their capabilities.
|
|
272
|
-
|
|
273
|
-
**Template:** Copy `wayfinder_paths/templates/strategy/manifest.yaml` as a starting point.
|
|
274
|
-
|
|
275
|
-
**Schema:**
|
|
276
|
-
|
|
277
|
-
```yaml
|
|
278
|
-
schema_version: "0.1"
|
|
279
|
-
entrypoint: "strategies.my_strategy.strategy.MyStrategy"
|
|
280
|
-
permissions:
|
|
281
|
-
policy: "(wallet.id == 'FORMAT_WALLET_ID') && (eth.tx.to == '0x...')"
|
|
282
|
-
adapters:
|
|
283
|
-
- name: "POOL"
|
|
284
|
-
capabilities: ["pool.read", "pool.analytics"]
|
|
285
|
-
- name: "BRAP"
|
|
286
|
-
capabilities: ["swap.quote", "swap.execute"]
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
**Fields:**
|
|
290
|
-
|
|
291
|
-
- `schema_version`: Manifest schema version (currently `"0.1"`)
|
|
292
|
-
- `entrypoint`: Full Python import path to the strategy class (required)
|
|
293
|
-
- `name`: Strategy directory name (optional, used for wallet lookup - defaults to directory name)
|
|
294
|
-
- `permissions.policy`: Security policy string that defines transaction permissions (required, non-empty)
|
|
295
|
-
- `adapters`: List of required adapters with their names and needed capabilities (required, non-empty)
|
|
296
|
-
- `name`: Adapter type identifier (e.g., "POOL", "BRAP")
|
|
297
|
-
- `capabilities`: List of capabilities required from this adapter
|
|
298
|
-
|
|
299
|
-
**Example** (`wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml`):
|
|
300
|
-
|
|
301
|
-
```yaml
|
|
302
|
-
schema_version: "0.1"
|
|
303
|
-
entrypoint: "strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy"
|
|
304
|
-
permissions:
|
|
305
|
-
policy: "(wallet.id == 'FORMAT_WALLET_ID') && ((eth.tx.data[0..10] == '0x095ea7b3' && eth.tx.data[34..74] == 'f75584ef6673ad213a685a1b58cc0330b8ea22cf') || (eth.tx.to == '0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf'))"
|
|
306
|
-
adapters:
|
|
307
|
-
- name: "BALANCE"
|
|
308
|
-
capabilities: ["wallet_read", "wallet_transfer"]
|
|
309
|
-
- name: "POOL"
|
|
310
|
-
capabilities: ["pool.read", "pool.analytics"]
|
|
311
|
-
- name: "BRAP"
|
|
312
|
-
capabilities: ["swap.quote", "swap.execute"]
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
#### Manifest Validation
|
|
316
|
-
|
|
317
|
-
Manifests are automatically validated to ensure:
|
|
318
|
-
|
|
319
|
-
- Schema compliance (all required fields present, correct types)
|
|
320
|
-
- Entrypoint classes exist and are importable
|
|
321
|
-
- Dependencies are valid client classes
|
|
322
|
-
- Permissions policies are non-empty
|
|
323
|
-
|
|
324
|
-
**Validate locally:**
|
|
325
|
-
|
|
326
|
-
```bash
|
|
327
|
-
# Validate all manifests
|
|
328
|
-
just validate-manifests
|
|
329
|
-
|
|
330
|
-
# Or manually
|
|
331
|
-
PYTHONPATH=wayfinder_paths poetry run python wayfinder_paths/scripts/validate_manifests.py
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Validation runs automatically in CI/CD on every PR and push to main. All manifests must be valid before merging.
|
|
335
|
-
|
|
336
|
-
**How Validation Works:**
|
|
337
|
-
|
|
338
|
-
The `validate_manifests.py` script performs multi-stage validation:
|
|
339
|
-
|
|
340
|
-
1. **Schema Validation** (via Pydantic models):
|
|
341
|
-
- Loads YAML file and validates against `AdapterManifest` or `StrategyManifest` schema
|
|
342
|
-
- Checks required fields, types, and basic constraints (e.g., capabilities cannot be empty)
|
|
343
|
-
- Validates entrypoint format (must be full import path like `"adapters.pool_adapter.adapter.PoolAdapter"`)
|
|
344
|
-
|
|
345
|
-
2. **Entrypoint Verification**:
|
|
346
|
-
- **For Adapters**: Imports the entrypoint class and verifies it's a subclass of `BaseAdapter`
|
|
347
|
-
- **For Strategies**: Imports the entrypoint class and verifies it's a subclass of `Strategy`
|
|
348
|
-
- Uses Python's `__import__()` to dynamically import the module and class
|
|
349
|
-
- Catches import errors, missing classes, and type mismatches
|
|
350
|
-
|
|
351
|
-
3. **Dependency Verification** (adapters only):
|
|
352
|
-
- Validates that all declared dependencies (e.g., `PoolClient`, `TokenClient`) exist in `core.clients`
|
|
353
|
-
- Attempts to import each dependency as `core.clients.{DepName}`
|
|
354
|
-
|
|
355
|
-
4. **Permissions Validation** (strategies only):
|
|
356
|
-
- Validated by Pydantic: ensures `permissions.policy` exists and is non-empty
|
|
357
|
-
- Policy syntax is not parsed/validated (assumed to be valid at runtime)
|
|
358
|
-
|
|
359
|
-
**Validation Flow:**
|
|
360
|
-
|
|
361
|
-
```
|
|
362
|
-
For each manifest file:
|
|
363
|
-
1. Load YAML → Parse with Pydantic (schema validation)
|
|
364
|
-
2. Import entrypoint class → Verify inheritance (entrypoint validation)
|
|
365
|
-
3. For adapters: Import dependencies → Verify they exist (dependency validation)
|
|
366
|
-
4. Collect all errors → Report results
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
The script automatically discovers all manifests by scanning:
|
|
370
|
-
|
|
371
|
-
- `wayfinder_paths/adapters/*/manifest.yaml` for adapter manifests
|
|
372
|
-
- `wayfinder_paths/strategies/*/manifest.yaml` for strategy manifests
|
|
373
|
-
|
|
374
|
-
All errors are collected and reported at the end, with the script exiting with code 1 if any validation fails.
|
|
375
|
-
|
|
376
|
-
#### Capabilities
|
|
377
|
-
|
|
378
|
-
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.
|
|
379
|
-
|
|
380
|
-
When creating an adapter:
|
|
381
|
-
|
|
382
|
-
1. Declare capabilities in your `manifest.yaml`
|
|
383
|
-
2. Implement methods that fulfill those capabilities
|
|
384
|
-
3. Capabilities are validated at manifest validation time (entrypoint must be importable)
|
|
385
|
-
|
|
386
210
|
### Configuration
|
|
387
211
|
|
|
388
212
|
Configuration is split between:
|
|
@@ -473,7 +297,7 @@ class MyAdapter(BaseAdapter):
|
|
|
473
297
|
self.pool_client = PoolClient()
|
|
474
298
|
|
|
475
299
|
async def connect(self) -> bool:
|
|
476
|
-
"""No-op for read-only adapters, but kept for
|
|
300
|
+
"""No-op for read-only adapters, but kept for interface consistency."""
|
|
477
301
|
return True
|
|
478
302
|
|
|
479
303
|
async def get_pools(self, pool_ids: list[str]):
|
|
@@ -546,6 +370,54 @@ class MyStrategy(Strategy):
|
|
|
546
370
|
}
|
|
547
371
|
```
|
|
548
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
|
+
|
|
549
421
|
### Built-in adapters
|
|
550
422
|
|
|
551
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.
|
|
@@ -554,8 +426,9 @@ class MyStrategy(Strategy):
|
|
|
554
426
|
- **LEDGER (LedgerAdapter)**: records deposits, withdrawals, custom operations, and cashflows via `LedgerClient`, and can read strategy transaction summaries.
|
|
555
427
|
- **TOKEN (TokenAdapter)**: lightweight wrapper around `TokenClient` for token metadata, live price snapshots, and gas token lookups.
|
|
556
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.
|
|
557
430
|
|
|
558
|
-
|
|
431
|
+
Strategies register the adapters they need in their `__init__` method. Adapters implement their specific capabilities and raise `NotImplementedError` for unsupported operations.
|
|
559
432
|
|
|
560
433
|
## 🧪 Testing
|
|
561
434
|
|