wayfinder-paths 0.1.1__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 (115) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +394 -0
  2. wayfinder_paths/__init__.py +21 -0
  3. wayfinder_paths/config.example.json +20 -0
  4. wayfinder_paths/conftest.py +31 -0
  5. wayfinder_paths/core/__init__.py +13 -0
  6. wayfinder_paths/core/adapters/BaseAdapter.py +48 -0
  7. wayfinder_paths/core/adapters/__init__.py +5 -0
  8. wayfinder_paths/core/adapters/base.py +5 -0
  9. wayfinder_paths/core/clients/AuthClient.py +83 -0
  10. wayfinder_paths/core/clients/BRAPClient.py +90 -0
  11. wayfinder_paths/core/clients/ClientManager.py +231 -0
  12. wayfinder_paths/core/clients/HyperlendClient.py +151 -0
  13. wayfinder_paths/core/clients/LedgerClient.py +222 -0
  14. wayfinder_paths/core/clients/PoolClient.py +96 -0
  15. wayfinder_paths/core/clients/SimulationClient.py +180 -0
  16. wayfinder_paths/core/clients/TokenClient.py +73 -0
  17. wayfinder_paths/core/clients/TransactionClient.py +47 -0
  18. wayfinder_paths/core/clients/WalletClient.py +90 -0
  19. wayfinder_paths/core/clients/WayfinderClient.py +258 -0
  20. wayfinder_paths/core/clients/__init__.py +48 -0
  21. wayfinder_paths/core/clients/protocols.py +295 -0
  22. wayfinder_paths/core/clients/sdk_example.py +115 -0
  23. wayfinder_paths/core/config.py +369 -0
  24. wayfinder_paths/core/constants/__init__.py +26 -0
  25. wayfinder_paths/core/constants/base.py +25 -0
  26. wayfinder_paths/core/constants/erc20_abi.py +118 -0
  27. wayfinder_paths/core/constants/hyperlend_abi.py +152 -0
  28. wayfinder_paths/core/engine/VaultJob.py +182 -0
  29. wayfinder_paths/core/engine/__init__.py +5 -0
  30. wayfinder_paths/core/engine/manifest.py +97 -0
  31. wayfinder_paths/core/services/__init__.py +0 -0
  32. wayfinder_paths/core/services/base.py +177 -0
  33. wayfinder_paths/core/services/local_evm_txn.py +429 -0
  34. wayfinder_paths/core/services/local_token_txn.py +231 -0
  35. wayfinder_paths/core/services/web3_service.py +45 -0
  36. wayfinder_paths/core/settings.py +61 -0
  37. wayfinder_paths/core/strategies/Strategy.py +183 -0
  38. wayfinder_paths/core/strategies/__init__.py +5 -0
  39. wayfinder_paths/core/strategies/base.py +7 -0
  40. wayfinder_paths/core/utils/__init__.py +1 -0
  41. wayfinder_paths/core/utils/evm_helpers.py +165 -0
  42. wayfinder_paths/core/utils/wallets.py +77 -0
  43. wayfinder_paths/core/wallets/README.md +91 -0
  44. wayfinder_paths/core/wallets/WalletManager.py +56 -0
  45. wayfinder_paths/core/wallets/__init__.py +7 -0
  46. wayfinder_paths/run_strategy.py +409 -0
  47. wayfinder_paths/scripts/__init__.py +0 -0
  48. wayfinder_paths/scripts/create_strategy.py +181 -0
  49. wayfinder_paths/scripts/make_wallets.py +160 -0
  50. wayfinder_paths/scripts/validate_manifests.py +213 -0
  51. wayfinder_paths/tests/__init__.py +0 -0
  52. wayfinder_paths/tests/test_smoke_manifest.py +48 -0
  53. wayfinder_paths/tests/test_test_coverage.py +212 -0
  54. wayfinder_paths/tests/test_utils.py +64 -0
  55. wayfinder_paths/vaults/__init__.py +0 -0
  56. wayfinder_paths/vaults/adapters/__init__.py +0 -0
  57. wayfinder_paths/vaults/adapters/balance_adapter/README.md +104 -0
  58. wayfinder_paths/vaults/adapters/balance_adapter/adapter.py +257 -0
  59. wayfinder_paths/vaults/adapters/balance_adapter/examples.json +6 -0
  60. wayfinder_paths/vaults/adapters/balance_adapter/manifest.yaml +8 -0
  61. wayfinder_paths/vaults/adapters/balance_adapter/test_adapter.py +83 -0
  62. wayfinder_paths/vaults/adapters/brap_adapter/README.md +249 -0
  63. wayfinder_paths/vaults/adapters/brap_adapter/__init__.py +7 -0
  64. wayfinder_paths/vaults/adapters/brap_adapter/adapter.py +717 -0
  65. wayfinder_paths/vaults/adapters/brap_adapter/examples.json +175 -0
  66. wayfinder_paths/vaults/adapters/brap_adapter/manifest.yaml +11 -0
  67. wayfinder_paths/vaults/adapters/brap_adapter/test_adapter.py +288 -0
  68. wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +7 -0
  69. wayfinder_paths/vaults/adapters/hyperlend_adapter/adapter.py +298 -0
  70. wayfinder_paths/vaults/adapters/hyperlend_adapter/manifest.yaml +10 -0
  71. wayfinder_paths/vaults/adapters/hyperlend_adapter/test_adapter.py +267 -0
  72. wayfinder_paths/vaults/adapters/ledger_adapter/README.md +158 -0
  73. wayfinder_paths/vaults/adapters/ledger_adapter/__init__.py +7 -0
  74. wayfinder_paths/vaults/adapters/ledger_adapter/adapter.py +286 -0
  75. wayfinder_paths/vaults/adapters/ledger_adapter/examples.json +131 -0
  76. wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +11 -0
  77. wayfinder_paths/vaults/adapters/ledger_adapter/test_adapter.py +202 -0
  78. wayfinder_paths/vaults/adapters/pool_adapter/README.md +218 -0
  79. wayfinder_paths/vaults/adapters/pool_adapter/__init__.py +7 -0
  80. wayfinder_paths/vaults/adapters/pool_adapter/adapter.py +289 -0
  81. wayfinder_paths/vaults/adapters/pool_adapter/examples.json +143 -0
  82. wayfinder_paths/vaults/adapters/pool_adapter/manifest.yaml +10 -0
  83. wayfinder_paths/vaults/adapters/pool_adapter/test_adapter.py +222 -0
  84. wayfinder_paths/vaults/adapters/token_adapter/README.md +101 -0
  85. wayfinder_paths/vaults/adapters/token_adapter/__init__.py +3 -0
  86. wayfinder_paths/vaults/adapters/token_adapter/adapter.py +92 -0
  87. wayfinder_paths/vaults/adapters/token_adapter/examples.json +26 -0
  88. wayfinder_paths/vaults/adapters/token_adapter/manifest.yaml +6 -0
  89. wayfinder_paths/vaults/adapters/token_adapter/test_adapter.py +135 -0
  90. wayfinder_paths/vaults/strategies/__init__.py +0 -0
  91. wayfinder_paths/vaults/strategies/config.py +85 -0
  92. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/README.md +99 -0
  93. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/examples.json +16 -0
  94. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/manifest.yaml +7 -0
  95. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/strategy.py +2328 -0
  96. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/test_strategy.py +319 -0
  97. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/README.md +95 -0
  98. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/examples.json +17 -0
  99. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/manifest.yaml +17 -0
  100. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/strategy.py +1684 -0
  101. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/test_strategy.py +350 -0
  102. wayfinder_paths/vaults/templates/adapter/README.md +105 -0
  103. wayfinder_paths/vaults/templates/adapter/adapter.py +26 -0
  104. wayfinder_paths/vaults/templates/adapter/examples.json +8 -0
  105. wayfinder_paths/vaults/templates/adapter/manifest.yaml +6 -0
  106. wayfinder_paths/vaults/templates/adapter/test_adapter.py +49 -0
  107. wayfinder_paths/vaults/templates/strategy/README.md +152 -0
  108. wayfinder_paths/vaults/templates/strategy/examples.json +11 -0
  109. wayfinder_paths/vaults/templates/strategy/manifest.yaml +8 -0
  110. wayfinder_paths/vaults/templates/strategy/strategy.py +57 -0
  111. wayfinder_paths/vaults/templates/strategy/test_strategy.py +197 -0
  112. wayfinder_paths-0.1.1.dist-info/LICENSE +21 -0
  113. wayfinder_paths-0.1.1.dist-info/METADATA +727 -0
  114. wayfinder_paths-0.1.1.dist-info/RECORD +115 -0
  115. wayfinder_paths-0.1.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,727 @@
1
+ Metadata-Version: 2.1
2
+ Name: wayfinder-paths
3
+ Version: 0.1.1
4
+ Summary: Wayfinder Path: strategies and adapters
5
+ Author: Wayfinder
6
+ Author-email: dev@wayfinder.ai
7
+ Requires-Python: >=3.12,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Requires-Dist: aiohttp (>=3.13.0,<4.0.0)
11
+ Requires-Dist: eth-account (>=0.13.7,<0.14.0)
12
+ Requires-Dist: httpx (>=0.28.1,<0.29.0)
13
+ Requires-Dist: loguru (>=0.7.3,<0.8.0)
14
+ Requires-Dist: numpy (>=1.26.0,<2.0.0)
15
+ Requires-Dist: pandas (>=2.2.0,<3.0.0)
16
+ Requires-Dist: pydantic (>=2.11.9,<3.0.0)
17
+ Requires-Dist: pydantic-settings (>=2.7.0,<3.0.0)
18
+ Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
19
+ Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
20
+ Requires-Dist: web3 (>=7.13.0,<8.0.0)
21
+ Description-Content-Type: text/markdown
22
+
23
+ # 🔐 Wayfinder Vaults
24
+
25
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/)
26
+ [![Docker](https://img.shields.io/badge/docker-ready-brightgreen.svg)](https://www.docker.com/)
27
+ [![Discord](https://img.shields.io/badge/discord-join-7289da.svg)](https://discord.gg/fUVwGMXjm3)
28
+
29
+ Open-source platform for community-contributed crypto trading strategies and adapters. Build, test, and deploy automated trading vaults with direct wallet integration.
30
+
31
+ ## 🚀 Quick Start
32
+
33
+ ```bash
34
+ # Clone the repository
35
+ git clone https://github.com/wayfinder-ai/wayfinder-paths.git
36
+ cd wayfinder-paths
37
+
38
+ # Install Poetry (if not already installed)
39
+ curl -sSL https://install.python-poetry.org | python3 -
40
+
41
+ # Install dependencies
42
+ poetry install
43
+
44
+ # ⚠️ Generate test wallets FIRST (required!)
45
+ # This creates wallets.json with live wallets for local testing
46
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault
47
+
48
+ # Copy and configure
49
+ cp wayfinder_paths/config.example.json config.json
50
+ # Edit config.json with your Wayfinder credentials
51
+
52
+ # Run a strategy locally (one-shot status check)
53
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --action status --config config.json
54
+
55
+ export WAYFINDER_API_KEY="sk_live_abc123..."
56
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --config config.json
57
+
58
+ # Run continuously (production mode)
59
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --config config.json
60
+ ```
61
+
62
+ ## 📁 Repository Structure
63
+
64
+ ```
65
+ wayfinder_paths/
66
+ ├── wayfinder_paths/ # Main package directory
67
+ │ ├── core/ # Core engine (maintained by team)
68
+ │ │ ├── clients/ # API client managers
69
+ │ │ ├── adapters/ # Base adapter interfaces
70
+ │ │ ├── engine/ # Trading engine & VaultJob
71
+ │ │ ├── strategies/ # Base strategy classes
72
+ │ │ └── config.py # Configuration system
73
+ │ ├── vaults/ # Community contributions (each artifact in its own folder)
74
+ │ │ ├── adapters/ # Your exchange/protocol integrations
75
+ │ │ │ ├── balance_adapter/
76
+ │ │ │ │ ├── adapter.py # Adapter implementation
77
+ │ │ │ │ ├── manifest.yaml # Adapter manifest (caps, entrypoint)
78
+ │ │ │ │ ├── examples.json # Example inputs for smoke
79
+ │ │ │ │ ├── README.md # Local notes
80
+ │ │ │ │ └── test_adapter.py # Local smoke test
81
+ │ │ │ ├── brap_adapter/
82
+ │ │ │ └── ...
83
+ │ │ └── strategies/ # Your trading strategies
84
+ │ │ ├── stablecoin_yield_strategy/
85
+ │ │ │ ├── strategy.py # Strategy implementation
86
+ │ │ │ ├── manifest.yaml # Strategy manifest
87
+ │ │ │ ├── examples.json # Example inputs
88
+ │ │ │ ├── README.md # Local notes
89
+ │ │ │ └── test_strategy.py # Local smoke test
90
+ │ │ └── ...
91
+ │ ├── tests/ # Test suite
92
+ │ ├── CONFIG_GUIDE.md # Configuration documentation
93
+ │ ├── config.example.json # Example configuration
94
+ │ ├── scripts/ # Utility scripts
95
+ │ └── run_strategy.py # Strategy runner script
96
+ ├── config.json # Your local config (created by you)
97
+ ├── wallets.json # Generated dev wallets
98
+ ├── pyproject.toml # Poetry configuration
99
+ └── README.md # This file
100
+ ```
101
+
102
+ ## 🤝 Contributing
103
+
104
+ We welcome contributions! This is an open-source project where community members can contribute adapters and strategies.
105
+
106
+ ### Quick Contribution Guide
107
+
108
+ 1. **Fork the repository** and clone your fork
109
+ 2. **Create a feature branch**: `git checkout -b feature/my-strategy`
110
+ 3. **Copy a template** to get started:
111
+ - **For adapters**: Copy `wayfinder_paths/vaults/templates/adapter/` to `wayfinder_paths/vaults/adapters/my_adapter/`
112
+ - **For strategies**: Copy `wayfinder_paths/vaults/templates/strategy/` to `wayfinder_paths/vaults/strategies/my_strategy/`
113
+ 4. **Customize** the template (rename classes, update manifest, implement methods)
114
+ 5. **Test your code** thoroughly using the provided test framework
115
+ 6. **Validate manifests**: Run `just validate-manifests`
116
+ 7. **Submit a Pull Request** with a clear description of your changes
117
+
118
+ ### What You Can Contribute
119
+
120
+ - **Adapters**: Exchange/protocol integrations (e.g., Uniswap, Aave, Compound)
121
+ - **Strategies**: Trading algorithms and yield optimization strategies
122
+ - **Improvements**: Bug fixes, documentation, or core system enhancements
123
+
124
+ ### Contributor Guidelines
125
+
126
+ #### For Adapters
127
+ - **Start from the template**: Copy `wayfinder_paths/vaults/templates/adapter/` as a starting point
128
+ - Extend `BaseAdapter` from `wayfinder_paths/core/adapters/BaseAdapter.py`
129
+ - Create a `manifest.yaml` (template at `wayfinder_paths/vaults/templates/adapter/manifest.yaml`) with:
130
+ - `entrypoint`: Full import path to your adapter class
131
+ - `capabilities`: List of capabilities your adapter provides
132
+ - `dependencies`: List of required client classes (e.g., `PoolClient`, `TokenClient`)
133
+ - Implement methods that fulfill the declared capabilities
134
+ - Add comprehensive tests in `test_adapter.py`
135
+ - Include usage examples in `examples.json`
136
+ - Document your adapter in `README.md`
137
+ - Validate your manifest: `just validate-manifests`
138
+
139
+ #### For Strategies
140
+ - **Start from the template**: Use `just create-strategy "Strategy Name"` to create a new strategy with its own wallet, or copy `wayfinder_paths/vaults/templates/strategy/` manually
141
+ - Extend `Strategy` from `wayfinder_paths/core/strategies/Strategy.py`
142
+ - Create a `manifest.yaml` (template at `wayfinder_paths/vaults/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
+ - Implement required methods: `deposit()`, `update()`, `status()`, `withdraw()`
148
+ - Include test cases in `test_strategy.py`
149
+ - Add example configurations in `examples.json`
150
+ - Validate your manifest: `just validate-manifests`
151
+
152
+ #### General Guidelines
153
+ - **Code Quality**: Follow existing patterns and use type hints
154
+ - **Testing**: See [TESTING.md](TESTING.md) - minimum: smoke test for strategies, basic tests for adapters
155
+ - **Documentation**: Update README files and add docstrings
156
+ - **Security**: Never hardcode API keys or private keys
157
+ - **Architecture**: Use adapters for external integrations, not direct API calls
158
+
159
+ ### Development Setup
160
+
161
+ ```bash
162
+ # 1. Fork and clone the repository
163
+ git clone https://github.com/yourusername/wayfinder-paths.git
164
+ cd wayfinder-paths
165
+
166
+ # 2. Install dependencies
167
+ poetry install
168
+
169
+ # 3. Generate test wallets (required before testing!)
170
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault
171
+
172
+ # 4. Create a new strategy (recommended - automatically creates wallet)
173
+ just create-strategy "My Strategy Name"
174
+
175
+ # Or manually copy a template:
176
+ # For adapters:
177
+ cp -r wayfinder_paths/vaults/templates/adapter wayfinder_paths/vaults/adapters/my_adapter
178
+ # For strategies:
179
+ cp -r wayfinder_paths/vaults/templates/strategy wayfinder_paths/vaults/strategies/my_strategy
180
+
181
+ # 5. Customize the template (see template README.md files for details)
182
+
183
+ # 6. Validate your manifest
184
+ just validate-manifests
185
+
186
+ # 7. Run tests
187
+ poetry run pytest -k smoke -v
188
+
189
+ # Or test your specific contribution
190
+ poetry run pytest wayfinder_paths/vaults/strategies/your_strategy/ -v
191
+ poetry run pytest wayfinder_paths/vaults/adapters/your_adapter/ -v
192
+
193
+ # 8. Test your contribution locally
194
+ poetry run python wayfinder_paths/run_strategy.py your_strategy --action status
195
+ ```
196
+
197
+ ### Getting Help
198
+
199
+ - 📖 Check existing adapters/strategies for examples
200
+ - 🐛 Open an issue for bugs or feature requests
201
+
202
+ ## 🏗️ Architecture
203
+
204
+ ### Client System
205
+ The platform uses a unified client system for all API interactions. Clients are thin wrappers that handle low-level API calls, authentication, and network communication. **Strategies should not call clients directly** - use adapters instead for domain-specific operations.
206
+
207
+ ### Clients vs Adapters
208
+
209
+ - **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`.
210
+ - **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.
211
+
212
+ Recommended usage:
213
+
214
+ - Strategies call adapters (not clients directly) for domain actions.
215
+ - Add or change a client when you need a new low-level capability shared across adapters.
216
+ - Add or change an adapter when integrating a new protocol/exchange or changing protocol-specific behavior.
217
+
218
+ Data flow: `Strategy` → `Adapter` → `Client(s)` → network/API.
219
+
220
+ ### Manifests
221
+
222
+ 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.
223
+
224
+ #### Adapter Manifests
225
+
226
+ Adapter manifests declare the capabilities an adapter provides and the clients it depends on.
227
+
228
+ **Template:** Copy `wayfinder_paths/vaults/templates/adapter/manifest.yaml` as a starting point.
229
+
230
+ **Schema:**
231
+ ```yaml
232
+ schema_version: "0.1"
233
+ entrypoint: "vaults.adapters.my_adapter.adapter.MyAdapter"
234
+ capabilities:
235
+ - "pool.read"
236
+ - "pool.analytics"
237
+ dependencies:
238
+ - "PoolClient"
239
+ - "TokenClient"
240
+ ```
241
+
242
+ **Fields:**
243
+ - `schema_version`: Manifest schema version (currently `"0.1"`)
244
+ - `entrypoint`: Full Python import path to the adapter class (required)
245
+ - `capabilities`: List of abstract capabilities this adapter provides (required, non-empty)
246
+ - `dependencies`: List of client class names from `core.clients` that this adapter requires (required, non-empty)
247
+
248
+ **Example** (`vaults/adapters/pool_adapter/manifest.yaml`):
249
+ ```yaml
250
+ schema_version: "0.1"
251
+ entrypoint: "vaults.adapters.pool_adapter.adapter.PoolAdapter"
252
+ capabilities:
253
+ - "pool.read"
254
+ - "pool.analytics"
255
+ - "pool.discovery"
256
+ - "llama.data"
257
+ - "pool.reports"
258
+ dependencies:
259
+ - "PoolClient"
260
+ ```
261
+
262
+ #### Strategy Manifests
263
+
264
+ Strategy manifests declare permissions and required adapters with their capabilities.
265
+
266
+ **Template:** Copy `wayfinder_paths/vaults/templates/strategy/manifest.yaml` as a starting point.
267
+
268
+ **Schema:**
269
+ ```yaml
270
+ schema_version: "0.1"
271
+ entrypoint: "vaults.strategies.my_strategy.strategy.MyStrategy"
272
+ permissions:
273
+ policy: "(wallet.id == 'FORMAT_WALLET_ID') && (eth.tx.to == '0x...')"
274
+ adapters:
275
+ - name: "POOL"
276
+ capabilities: ["pool.read", "pool.analytics"]
277
+ - name: "BRAP"
278
+ capabilities: ["swap.quote", "swap.execute"]
279
+ ```
280
+
281
+ **Fields:**
282
+ - `schema_version`: Manifest schema version (currently `"0.1"`)
283
+ - `entrypoint`: Full Python import path to the strategy class (required)
284
+ - `name`: Strategy directory name (optional, used for wallet lookup - defaults to directory name)
285
+ - `permissions.policy`: Security policy string that defines transaction permissions (required, non-empty)
286
+ - `adapters`: List of required adapters with their names and needed capabilities (required, non-empty)
287
+ - `name`: Adapter type identifier (e.g., "POOL", "BRAP")
288
+ - `capabilities`: List of capabilities required from this adapter
289
+
290
+ **Example** (`vaults/strategies/stablecoin_yield_strategy/manifest.yaml`):
291
+ ```yaml
292
+ schema_version: "0.1"
293
+ entrypoint: "vaults.strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy"
294
+ permissions:
295
+ policy: "(wallet.id == 'FORMAT_WALLET_ID') && ((eth.tx.data[0..10] == '0x095ea7b3' && eth.tx.data[34..74] == 'f75584ef6673ad213a685a1b58cc0330b8ea22cf') || (eth.tx.to == '0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf'))"
296
+ adapters:
297
+ - name: "BALANCE"
298
+ capabilities: ["wallet_read", "wallet_transfer"]
299
+ - name: "POOL"
300
+ capabilities: ["pool.read", "pool.analytics"]
301
+ - name: "BRAP"
302
+ capabilities: ["swap.quote", "swap.execute"]
303
+ ```
304
+
305
+ #### Manifest Validation
306
+
307
+ Manifests are automatically validated to ensure:
308
+ - Schema compliance (all required fields present, correct types)
309
+ - Entrypoint classes exist and are importable
310
+ - Dependencies are valid client classes
311
+ - Permissions policies are non-empty
312
+
313
+ **Validate locally:**
314
+ ```bash
315
+ # Validate all manifests
316
+ just validate-manifests
317
+
318
+ # Or manually
319
+ PYTHONPATH=wayfinder_paths poetry run python wayfinder_paths/scripts/validate_manifests.py
320
+ ```
321
+
322
+ Validation runs automatically in CI/CD on every PR and push to main. All manifests must be valid before merging.
323
+
324
+ **How Validation Works:**
325
+
326
+ The `validate_manifests.py` script performs multi-stage validation:
327
+
328
+ 1. **Schema Validation** (via Pydantic models):
329
+ - Loads YAML file and validates against `AdapterManifest` or `StrategyManifest` schema
330
+ - Checks required fields, types, and basic constraints (e.g., capabilities cannot be empty)
331
+ - Validates entrypoint format (must be full import path like `"vaults.adapters.pool_adapter.adapter.PoolAdapter"`)
332
+
333
+ 2. **Entrypoint Verification**:
334
+ - **For Adapters**: Imports the entrypoint class and verifies it's a subclass of `BaseAdapter`
335
+ - **For Strategies**: Imports the entrypoint class and verifies it's a subclass of `Strategy`
336
+ - Uses Python's `__import__()` to dynamically import the module and class
337
+ - Catches import errors, missing classes, and type mismatches
338
+
339
+ 3. **Dependency Verification** (adapters only):
340
+ - Validates that all declared dependencies (e.g., `PoolClient`, `TokenClient`) exist in `core.clients`
341
+ - Attempts to import each dependency as `core.clients.{DepName}`
342
+
343
+ 4. **Permissions Validation** (strategies only):
344
+ - Validated by Pydantic: ensures `permissions.policy` exists and is non-empty
345
+ - Policy syntax is not parsed/validated (assumed to be valid at runtime)
346
+
347
+ **Validation Flow:**
348
+ ```
349
+ For each manifest file:
350
+ 1. Load YAML → Parse with Pydantic (schema validation)
351
+ 2. Import entrypoint class → Verify inheritance (entrypoint validation)
352
+ 3. For adapters: Import dependencies → Verify they exist (dependency validation)
353
+ 4. Collect all errors → Report results
354
+ ```
355
+
356
+ The script automatically discovers all manifests by scanning:
357
+ - `wayfinder_paths/vaults/adapters/*/manifest.yaml` for adapter manifests
358
+ - `wayfinder_paths/vaults/strategies/*/manifest.yaml` for strategy manifests
359
+
360
+ All errors are collected and reported at the end, with the script exiting with code 1 if any validation fails.
361
+
362
+ #### Capabilities
363
+
364
+ 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.
365
+
366
+ When creating an adapter:
367
+ 1. Declare capabilities in your `manifest.yaml`
368
+ 2. Implement methods that fulfill those capabilities
369
+ 3. Capabilities are validated at manifest validation time (entrypoint must be importable)
370
+
371
+ ### Configuration
372
+ Configuration is split between:
373
+ - **User Config**: Your credentials and preferences
374
+ - **System Config**: Platform settings
375
+ - **Strategy Config**: Strategy-specific parameters
376
+
377
+ See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for details.
378
+
379
+ ### Authentication
380
+
381
+ Wayfinder Vaults supports two authentication methods:
382
+
383
+ #### 1. Service Account Authentication (API Key)
384
+ For backend services and automated systems with higher rate limits:
385
+
386
+ **Option A: Pass to Strategy Constructor**
387
+ ```python
388
+ from wayfinder_paths.vaults.strategies.stablecoin_yield_strategy.strategy import StablecoinYieldStrategy
389
+
390
+ strategy = StablecoinYieldStrategy(
391
+ config={...},
392
+ api_key="sk_live_abc123..." # API key is auto-discovered by all clients
393
+ )
394
+ ```
395
+
396
+ **Option B: Set Environment Variable**
397
+ ```bash
398
+ export WAYFINDER_API_KEY="sk_live_abc123..."
399
+ # All clients will automatically discover and use this
400
+ ```
401
+
402
+ **Option C: Add to config.json**
403
+ ```json
404
+ {
405
+ "user": {
406
+ "api_key": "sk_live_abc123..."
407
+ },
408
+ "system": {
409
+ "api_key": "sk_live_abc123..." // Alternative: system-level API key
410
+ }
411
+ }
412
+ ```
413
+
414
+ **Priority Order:** Constructor parameter > `config.json` > `WAYFINDER_API_KEY` environment variable
415
+
416
+ #### 2. Personal Access Authentication (OAuth)
417
+ For standalone SDK users with username/password:
418
+
419
+ ```json
420
+ {
421
+ "user": {
422
+ "username": "your_username",
423
+ "password": "your_password",
424
+ "refresh_token": null // Optional: use refresh token instead
425
+ }
426
+ }
427
+ ```
428
+
429
+ **How It Works:**
430
+ - API keys are automatically discovered by all clients (no need to pass explicitly)
431
+ - When an API key is available, it's used for all API requests (including public endpoints) for rate limiting
432
+ - If no API key is found, the system falls back to OAuth authentication
433
+ - All clients created by adapters automatically inherit the API key discovery mechanism
434
+
435
+ See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed authentication documentation.
436
+
437
+ ## 🔌 Creating Adapters
438
+
439
+ Adapters connect to exchanges and DeFi protocols using the client system.
440
+
441
+ ```python
442
+ # wayfinder_paths/vaults/adapters/my_adapter/adapter.py
443
+ from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
444
+ from wayfinder_paths.core.clients.PoolClient import PoolClient
445
+
446
+
447
+ class MyAdapter(BaseAdapter):
448
+ """Thin wrapper around PoolClient that exposes pool metadata to strategies."""
449
+
450
+ adapter_type = "POOL"
451
+
452
+ def __init__(self, config: dict | None = None):
453
+ super().__init__("my_adapter", config)
454
+ self.pool_client = PoolClient()
455
+
456
+ async def connect(self) -> bool:
457
+ """No-op for read-only adapters, but kept for manifest compatibility."""
458
+ return True
459
+
460
+ async def get_pools(self, pool_ids: list[str]):
461
+ data = await self.pool_client.get_pools_by_ids(
462
+ pool_ids=",".join(pool_ids), merge_external=True
463
+ )
464
+ return (True, data)
465
+ ```
466
+
467
+ ## 📈 Building Strategies
468
+
469
+ Strategies implement trading logic using adapters and the unified client system.
470
+
471
+ ```python
472
+ # wayfinder_paths/vaults/strategies/my_strategy/strategy.py
473
+ from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
474
+ from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
475
+ from wayfinder_paths.vaults.adapters.balance_adapter.adapter import BalanceAdapter
476
+
477
+
478
+ class MyStrategy(Strategy):
479
+ name = "Demo Strategy"
480
+
481
+ def __init__(
482
+ self,
483
+ config: dict | None = None,
484
+ *,
485
+ api_key: str | None = None, # Optional: API key for service account auth
486
+ ):
487
+ super().__init__(api_key=api_key) # Pass to base class for auto-discovery
488
+ self.config = config or {}
489
+ web3_service = DefaultWeb3Service(self.config)
490
+ # Adapters automatically discover API key from env var (set by Strategy.__init__)
491
+ balance_adapter = BalanceAdapter(self.config, web3_service=web3_service)
492
+ self.register_adapters([balance_adapter])
493
+ self.balance_adapter = balance_adapter
494
+
495
+ async def deposit(
496
+ self, main_token_amount: float = 0.0, gas_token_amount: float = 0.0
497
+ ) -> StatusTuple:
498
+ """Move funds from main wallet into the vault wallet."""
499
+ if main_token_amount <= 0:
500
+ return (False, "Nothing to deposit")
501
+
502
+ success, _ = await self.balance_adapter.get_balance(
503
+ token_id=self.config.get("token_id"),
504
+ wallet_address=self.config.get("main_wallet", {}).get("address"),
505
+ )
506
+ if not success:
507
+ return (False, "Unable to fetch balances")
508
+
509
+ # Use BalanceAdapter (which leverages LocalTokenTxnService builders) for transfers here.
510
+ self.last_deposit = main_token_amount
511
+ return (True, f"Deposited {main_token_amount} tokens")
512
+
513
+ async def update(self) -> StatusTuple:
514
+ """Periodic strategy update"""
515
+ return (True, "No-op update")
516
+
517
+ async def _status(self) -> StatusDict:
518
+ """Report balances back to the runner"""
519
+ success, balance = await self.balance_adapter.get_balance(
520
+ token_id=self.config.get("token_id"),
521
+ wallet_address=self.config.get("vault_wallet", {}).get("address"),
522
+ )
523
+ return {
524
+ "portfolio_value": float(balance or 0),
525
+ "net_deposit": float(getattr(self, "last_deposit", 0.0)),
526
+ "strategy_status": {"message": "healthy" if success else "unknown"},
527
+ }
528
+ ```
529
+
530
+ ### Built-in adapters
531
+
532
+ - **BALANCE (BalanceAdapter)**: wraps `WalletClient`/`TokenClient` to read wallet, token, and pool balances and now orchestrates transfers between the main/vault wallets with ledger bookkeeping. Requires a `Web3Service` so it can share the same wallet provider as the strategy.
533
+ - **POOL (PoolAdapter)**: composes `PoolClient` to fetch pools, llama analytics, combined reports, high-yield searches, and search helpers.
534
+ - **BRAP (BRAPAdapter)**: integrates the cross-chain quote service for swaps/bridges, including fee breakdowns, route comparisons, validation helpers, and swap execution/ledger recording when provided a `Web3Service`.
535
+ - **LEDGER (LedgerAdapter)**: records deposits, withdrawals, custom operations, and cashflows via `LedgerClient`, and can read vault transaction summaries.
536
+ - **TOKEN (TokenAdapter)**: lightweight wrapper around `TokenClient` for token metadata, live price snapshots, and gas token lookups.
537
+ - **HYPERLEND (HyperlendAdapter)**: connects to `HyperlendClient` for lending/supply caps inside the HyperLend strategy.
538
+
539
+ 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).
540
+
541
+ ## 🧪 Testing
542
+
543
+ **📖 For detailed testing guidance, see [TESTING.md](TESTING.md)**
544
+
545
+ ### Quick Start
546
+
547
+ ```bash
548
+ # 1. Generate test wallets (required!)
549
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault
550
+
551
+ # 2. Run smoke tests
552
+ poetry run pytest -k smoke -v
553
+
554
+ # 3. Test your specific contribution
555
+ poetry run pytest wayfinder_paths/vaults/strategies/my_strategy/ -v # Strategy
556
+ poetry run pytest wayfinder_paths/vaults/adapters/my_adapter/ -v # Adapter
557
+ ```
558
+
559
+ ### Testing Your Contribution
560
+
561
+ **Strategies**: Add a simple smoke test in `test_strategy.py` that exercises deposit → update → status → withdraw.
562
+
563
+ **Adapters**: Add basic functionality tests with mocked dependencies. Use `examples.json` to drive your tests.
564
+
565
+ See [TESTING.md](TESTING.md) for complete examples and best practices.
566
+
567
+ ## 💻 Local Development
568
+
569
+ ### Setup
570
+
571
+ ```bash
572
+ # Clone repo
573
+ git clone https://github.com/wayfinder-ai/wayfinder-paths.git
574
+ cd wayfinder-paths
575
+
576
+ # Install dependencies
577
+ poetry install
578
+
579
+ # Generate test wallets (essential!)
580
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault
581
+
582
+ # Copy and configure
583
+ cp wayfinder_paths/config.example.json config.json
584
+ # Edit config.json with your Wayfinder credentials
585
+
586
+ # Run a strategy (status check)
587
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --action status --config config.json
588
+
589
+ # Run with custom config
590
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --config my_config.json
591
+
592
+ # Run continuously with debug output
593
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --debug --config config.json
594
+ ```
595
+
596
+ ### Wallet Generation for Testing
597
+
598
+ **Before running any strategies, generate test wallets.** This creates `wallets.json` in the repository root with throwaway wallets for local testing:
599
+
600
+ ```bash
601
+ # Essential: Create default and vault wallets for testing
602
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault
603
+ ```
604
+
605
+ This creates:
606
+ - `default` wallet - your main wallet for testing
607
+ - `wallets.json` - wallet addresses and private keys for local testing
608
+
609
+ **Important:** These wallets are for testing only. Never use them with real funds or on mainnet.
610
+
611
+ **Per-Strategy Wallets:** Each strategy should have its own dedicated wallet. When you create a new strategy using `just create-strategy`, a wallet is automatically generated with a label matching the strategy directory name. The system automatically uses this wallet when running the strategy. See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for details.
612
+
613
+ Additional options:
614
+
615
+ ```bash
616
+ # Add 3 extra unlabeled wallets for multi-account testing
617
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault -n 3
618
+
619
+ # Generate keystore files (for geth/web3 compatibility)
620
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault --keystore-password "my-password"
621
+
622
+ # Replace existing wallets (if you need fresh test wallets)
623
+ poetry run python wayfinder_paths/scripts/make_wallets.py --default --vault --override
624
+ ```
625
+
626
+ ### Configuration
627
+
628
+ See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed configuration documentation.
629
+
630
+ #### Setup Configuration
631
+
632
+ ```bash
633
+ # Copy example config
634
+ cp wayfinder_paths/config.example.json config.json
635
+
636
+ # Edit config.json with your settings
637
+ # Required fields:
638
+ # - user.username: Your Wayfinder username
639
+ # - user.password: Your Wayfinder password
640
+ # - OR user.refresh_token: Your refresh token
641
+ # - system.wallets_path: Path to wallets.json (default: "wallets.json")
642
+ #
643
+ # Wallet addresses are auto-loaded from wallets.json by default.
644
+ # Then run with:
645
+ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --config config.json
646
+ ```
647
+
648
+ ## 📦 Publishing
649
+
650
+ This package is published to [PyPI](https://pypi.org/project/wayfinder-paths/) (Python Package Index).
651
+
652
+ ### How PyPI Works
653
+
654
+ - **Public by default**: Once published, anyone can download and install your package with `pip install wayfinder-paths` (no authentication required)
655
+ - **Who can update**: Only package owners and maintainers can publish new versions
656
+ - **Version control**: Version numbers must be unique and incrementing (you cannot overwrite a published version)
657
+ - **Access control**: You control who can publish by adding collaborators on PyPI (project owners can add maintainers)
658
+
659
+ ### Publishing a New Version
660
+
661
+ 1. **Update the version** in `pyproject.toml`:
662
+ ```toml
663
+ [tool.poetry]
664
+ version = "0.1.2" # Must be higher than previous version
665
+ ```
666
+
667
+ 2. **Set your PyPI token**:
668
+ ```bash
669
+ export PUBLISH_TOKEN="your_pypi_token_here"
670
+ # Or add to .env file: PUBLISH_TOKEN=your_pypi_token_here
671
+ ```
672
+
673
+ Get your PyPI token from: https://pypi.org/manage/account/token/
674
+
675
+ **Note**: PyPI requires 2FA (two-factor authentication) for all accounts.
676
+
677
+ 3. **Publish**:
678
+ ```bash
679
+ just publish
680
+ ```
681
+
682
+ This will:
683
+ - Build the package (wheel and source distribution)
684
+ - Publish to PyPI
685
+ - Make it publicly available for installation
686
+
687
+ ### Installing the Published Package
688
+
689
+ Anyone can install the published package with:
690
+ ```bash
691
+ pip install wayfinder-paths
692
+ ```
693
+
694
+ Or with Poetry:
695
+ ```bash
696
+ poetry add wayfinder-paths
697
+ ```
698
+
699
+ ### Managing Package Access
700
+
701
+ To add collaborators who can publish updates:
702
+ 1. Go to https://pypi.org/project/wayfinder-paths/
703
+ 2. Click "Manage" → "Collaborators"
704
+ 3. Add users as "Maintainers" (can publish) or "Owners" (full control)
705
+
706
+ ## 🔒 Security
707
+
708
+ - **Never hardcode API keys or Private keys** - use config.json for credentials
709
+ - **Never commit config.json** - add it to .gitignore
710
+ - **Test on testnet first** - use test network when available
711
+ - **Validate all inputs** - sanitize user data
712
+ - **Set gas limits** - prevent excessive fees
713
+
714
+ ## 📊 Backtesting
715
+
716
+ Coming soon
717
+
718
+ ## 🌟 Community
719
+
720
+ Need help or want to discuss strategies? Join our [Discord](https://discord.gg/fUVwGMXjm3)!
721
+
722
+ ## 📄 License
723
+
724
+ MIT License - see [LICENSE](LICENSE) file for details
725
+
726
+ 🚀 **Happy Wayfinding!**
727
+