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