wayfinder-paths 0.1.3__py3-none-any.whl → 0.1.5__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 +37 -32
- wayfinder_paths/__init__.py +3 -3
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/README.md +12 -12
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/adapter.py +12 -11
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/examples.json +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/test_adapter.py +12 -6
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/README.md +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/adapter.py +30 -23
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/test_adapter.py +2 -2
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +7 -0
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/adapter.py +33 -26
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/test_adapter.py +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/README.md +27 -40
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/adapter.py +78 -75
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/examples.json +10 -4
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +11 -0
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/test_adapter.py +33 -28
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/README.md +2 -14
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/adapter.py +12 -19
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/test_adapter.py +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/README.md +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/adapter.py +8 -4
- wayfinder_paths/adapters/token_adapter/examples.json +26 -0
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/test_adapter.py +1 -1
- wayfinder_paths/config.example.json +3 -1
- wayfinder_paths/core/__init__.py +3 -3
- wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
- wayfinder_paths/core/adapters/models.py +41 -0
- wayfinder_paths/core/clients/BRAPClient.py +21 -2
- wayfinder_paths/core/clients/ClientManager.py +42 -63
- wayfinder_paths/core/clients/HyperlendClient.py +46 -5
- wayfinder_paths/core/clients/LedgerClient.py +350 -124
- wayfinder_paths/core/clients/PoolClient.py +51 -19
- wayfinder_paths/core/clients/SimulationClient.py +16 -4
- wayfinder_paths/core/clients/TokenClient.py +34 -18
- wayfinder_paths/core/clients/TransactionClient.py +18 -2
- wayfinder_paths/core/clients/WalletClient.py +35 -4
- wayfinder_paths/core/clients/WayfinderClient.py +16 -5
- wayfinder_paths/core/clients/protocols.py +69 -62
- wayfinder_paths/core/clients/sdk_example.py +0 -5
- wayfinder_paths/core/config.py +192 -103
- wayfinder_paths/core/constants/base.py +17 -0
- wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
- wayfinder_paths/core/engine/__init__.py +2 -2
- wayfinder_paths/core/engine/manifest.py +1 -1
- wayfinder_paths/core/services/base.py +6 -4
- wayfinder_paths/core/services/local_evm_txn.py +3 -2
- wayfinder_paths/core/settings.py +2 -2
- wayfinder_paths/core/strategies/Strategy.py +123 -37
- wayfinder_paths/core/utils/evm_helpers.py +12 -10
- wayfinder_paths/core/wallets/README.md +3 -3
- wayfinder_paths/core/wallets/WalletManager.py +3 -3
- wayfinder_paths/{vaults/policies → policies}/enso.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/hyper_evm.py +2 -2
- wayfinder_paths/{vaults/policies → policies}/hyperlend.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/moonwell.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/prjx.py +1 -1
- wayfinder_paths/run_strategy.py +29 -27
- wayfinder_paths/scripts/create_strategy.py +3 -3
- wayfinder_paths/scripts/make_wallets.py +6 -6
- wayfinder_paths/scripts/validate_manifests.py +2 -2
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/README.md +10 -9
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/manifest.yaml +1 -1
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/strategy.py +47 -167
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/test_strategy.py +10 -8
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/README.md +15 -14
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/manifest.yaml +2 -2
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/strategy.py +97 -97
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/test_strategy.py +8 -8
- wayfinder_paths/{vaults/templates → templates}/adapter/README.md +5 -5
- wayfinder_paths/{vaults/templates → templates}/adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/templates → templates}/adapter/test_adapter.py +1 -1
- wayfinder_paths/{vaults/templates → templates}/strategy/README.md +10 -9
- wayfinder_paths/{vaults/templates → templates}/strategy/manifest.yaml +1 -1
- wayfinder_paths/{vaults/templates → templates}/strategy/test_strategy.py +8 -8
- wayfinder_paths/tests/test_test_coverage.py +5 -5
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/METADATA +146 -69
- wayfinder_paths-0.1.5.dist-info/RECORD +126 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +0 -7
- wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +0 -11
- wayfinder_paths/vaults/adapters/token_adapter/examples.json +0 -26
- wayfinder_paths/vaults/strategies/__init__.py +0 -0
- wayfinder_paths-0.1.3.dist-info/RECORD +0 -126
- /wayfinder_paths/{vaults → adapters}/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/token_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/erc20.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/evm.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/hyperliquid.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/util.py +0 -0
- /wayfinder_paths/{vaults/adapters → strategies}/__init__.py +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/config.py +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/adapter/adapter.py +0 -0
- /wayfinder_paths/{vaults/templates → templates}/adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/strategy/strategy.py +0 -0
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/WHEEL +0 -0
wayfinder_paths/CONFIG_GUIDE.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Configuration Guide
|
|
2
2
|
|
|
3
|
-
This guide explains how to configure your
|
|
3
|
+
This guide explains how to configure your strategies for local testing.
|
|
4
4
|
|
|
5
5
|
## Quick Setup
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# 1. Generate test wallets (required!)
|
|
9
|
-
|
|
9
|
+
# Creates a main wallet (or use 'just create-strategy' which auto-creates wallets)
|
|
10
|
+
just create-wallets
|
|
11
|
+
# Or manually: poetry run python wayfinder_paths/scripts/make_wallets.py -n 1
|
|
10
12
|
|
|
11
13
|
# 2. Create your config file
|
|
12
14
|
cp wayfinder_paths/config.example.json config.json
|
|
@@ -25,11 +27,11 @@ cp wayfinder_paths/config.example.json config.json
|
|
|
25
27
|
"username": "your_username", // OPTIONAL: For OAuth authentication
|
|
26
28
|
"password": "your_password", // OPTIONAL: For OAuth authentication
|
|
27
29
|
"refresh_token": null, // OPTIONAL: Alternative to username/password
|
|
28
|
-
"api_key": "sk_live_abc123..." // OPTIONAL: For service account authentication
|
|
30
|
+
"api_key": "sk_live_abc123..." // OPTIONAL: For service account authentication (loaded directly by clients, not stored in UserConfig)
|
|
29
31
|
},
|
|
30
32
|
"system": {
|
|
31
33
|
"api_base_url": "https://wayfinder.ai/api/v1",
|
|
32
|
-
"api_key": "sk_live_abc123...", // OPTIONAL: System-level API key (alternative to user.api_key)
|
|
34
|
+
"api_key": "sk_live_abc123...", // OPTIONAL: System-level API key (alternative to user.api_key, loaded directly by clients)
|
|
33
35
|
"wallets_path": "wallets.json" // Path to your generated wallets.json
|
|
34
36
|
},
|
|
35
37
|
"strategy": {
|
|
@@ -48,11 +50,13 @@ cp wayfinder_paths/config.example.json config.json
|
|
|
48
50
|
**Authentication (choose one method):**
|
|
49
51
|
|
|
50
52
|
**Option 1: Service Account (API Key) - Recommended for Production**
|
|
51
|
-
- `user.api_key` - Your Wayfinder API key for service account authentication
|
|
52
|
-
- OR `system.api_key` - System-level API key (alternative location)
|
|
53
|
+
- `user.api_key` - Your Wayfinder API key for service account authentication (loaded directly by clients from config.json)
|
|
54
|
+
- OR `system.api_key` - System-level API key (alternative location, loaded directly by clients)
|
|
53
55
|
- OR set `WAYFINDER_API_KEY` environment variable
|
|
54
56
|
- OR pass `api_key` parameter to strategy constructor
|
|
55
57
|
|
|
58
|
+
**Note:** API keys in `config.json` are loaded directly by `WayfinderClient` via `_load_config_credentials()`, not through the `UserConfig` or `SystemConfig` dataclasses. This is intentional to allow flexible credential loading.
|
|
59
|
+
|
|
56
60
|
**Option 2: Personal Access (OAuth) - For Development**
|
|
57
61
|
- `user.username` - Your Wayfinder backend username
|
|
58
62
|
- `user.password` - Your Wayfinder backend password
|
|
@@ -60,7 +64,7 @@ cp wayfinder_paths/config.example.json config.json
|
|
|
60
64
|
|
|
61
65
|
**Other Optional fields:**
|
|
62
66
|
- `user.main_wallet_address` - Override auto-loaded main wallet
|
|
63
|
-
- `user.
|
|
67
|
+
- `user.strategy_wallet_address` - Override auto-loaded strategy wallet
|
|
64
68
|
|
|
65
69
|
**Security Note:** Never commit `config.json` to version control. Add it to `.gitignore`.
|
|
66
70
|
|
|
@@ -70,7 +74,7 @@ These are managed automatically by Wayfinder:
|
|
|
70
74
|
- `api_base_url` - Wayfinder backend API endpoint
|
|
71
75
|
- `wallets_path` - Location of generated wallets.json
|
|
72
76
|
- `job_id` - Auto-generated by Wayfinder
|
|
73
|
-
- `job_type` - Set to "
|
|
77
|
+
- `job_type` - Set to "strategy"
|
|
74
78
|
|
|
75
79
|
## Strategy Configuration
|
|
76
80
|
|
|
@@ -93,24 +97,24 @@ Individual strategies may have their own configuration parameters. Check the str
|
|
|
93
97
|
Use the built-in script to generate test wallets for local development:
|
|
94
98
|
|
|
95
99
|
```bash
|
|
96
|
-
# Generate
|
|
97
|
-
poetry run python wayfinder_paths/scripts/make_wallets.py
|
|
100
|
+
# Generate main wallet (recommended for initial setup)
|
|
101
|
+
poetry run python wayfinder_paths/scripts/make_wallets.py -n 1
|
|
98
102
|
|
|
99
103
|
# Add additional wallets for multi-account testing
|
|
100
|
-
poetry run python wayfinder_paths/scripts/make_wallets.py
|
|
104
|
+
poetry run python wayfinder_paths/scripts/make_wallets.py -n 3
|
|
101
105
|
|
|
102
|
-
#
|
|
103
|
-
poetry run python wayfinder_paths/scripts/make_wallets.py --
|
|
106
|
+
# Create a wallet with a specific label (e.g., for a strategy)
|
|
107
|
+
poetry run python wayfinder_paths/scripts/make_wallets.py --label "my_strategy_name"
|
|
104
108
|
|
|
105
109
|
# Generate keystore files (for geth/web3 compatibility)
|
|
106
|
-
poetry run python wayfinder_paths/scripts/make_wallets.py
|
|
110
|
+
poetry run python wayfinder_paths/scripts/make_wallets.py -n 1 --keystore-password "your-password"
|
|
107
111
|
```
|
|
108
112
|
|
|
109
113
|
This creates `wallets.json` in the repository root with:
|
|
110
|
-
- **
|
|
111
|
-
- Additional
|
|
114
|
+
- **main** wallet - your main wallet for testing (labeled "main")
|
|
115
|
+
- Additional wallets with labels if specified
|
|
112
116
|
|
|
113
|
-
**Note:** Generated wallets are for testing only. Wallet addresses are automatically loaded from `wallets.json` when not explicitly set in config.
|
|
117
|
+
**Note:** Generated wallets are for testing only. Wallet addresses are automatically loaded from `wallets.json` when not explicitly set in config. Strategy-specific wallets are automatically created when you use `just create-strategy "Strategy Name"`.
|
|
114
118
|
|
|
115
119
|
## Per-Strategy Wallets
|
|
116
120
|
|
|
@@ -121,8 +125,8 @@ Each strategy should have its own dedicated wallet for isolation and security. T
|
|
|
121
125
|
When you run a strategy:
|
|
122
126
|
1. The system uses the strategy directory name (e.g., `hyperlend_stable_yield_strategy`) to look up a wallet
|
|
123
127
|
2. It searches `wallets.json` for a wallet with a matching `label`
|
|
124
|
-
3. If found, that wallet is used as the strategy's
|
|
125
|
-
4. Falls back to `
|
|
128
|
+
3. If found, that wallet is used as the strategy's dedicated wallet
|
|
129
|
+
4. Falls back to `strategy_wallet_address` from config if explicitly provided
|
|
126
130
|
|
|
127
131
|
### Creating a Strategy with Wallet
|
|
128
132
|
|
|
@@ -147,7 +151,7 @@ Wallets in `wallets.json` are stored with labels that match strategy directory n
|
|
|
147
151
|
{
|
|
148
152
|
"address": "0x...",
|
|
149
153
|
"private_key_hex": "...",
|
|
150
|
-
"label": "
|
|
154
|
+
"label": "main"
|
|
151
155
|
},
|
|
152
156
|
{
|
|
153
157
|
"address": "0x...",
|
|
@@ -184,11 +188,11 @@ Strategies access wallets the same way as before:
|
|
|
184
188
|
|
|
185
189
|
```python
|
|
186
190
|
# In strategy code
|
|
187
|
-
|
|
191
|
+
strategy_address = self.config.get("strategy_wallet").get("address")
|
|
188
192
|
main_address = self.config.get("main_wallet").get("address")
|
|
189
193
|
```
|
|
190
194
|
|
|
191
|
-
The
|
|
195
|
+
The strategy wallet is automatically populated from the wallet with label matching the strategy directory name.
|
|
192
196
|
|
|
193
197
|
## Loading Configuration
|
|
194
198
|
|
|
@@ -202,14 +206,14 @@ For programmatic use:
|
|
|
202
206
|
|
|
203
207
|
```python
|
|
204
208
|
from pathlib import Path
|
|
205
|
-
from core.config import
|
|
209
|
+
from core.config import StrategyJobConfig
|
|
206
210
|
import json
|
|
207
211
|
|
|
208
212
|
# Load from file
|
|
209
213
|
with open("config.json") as f:
|
|
210
214
|
config_data = json.load(f)
|
|
211
215
|
|
|
212
|
-
config =
|
|
216
|
+
config = StrategyJobConfig.from_dict(config_data)
|
|
213
217
|
|
|
214
218
|
# Configuration now has:
|
|
215
219
|
# - config.user.username & password (for Wayfinder backend)
|
|
@@ -219,13 +223,13 @@ config = VaultConfig.from_dict(config_data)
|
|
|
219
223
|
|
|
220
224
|
## Wallet Abstraction
|
|
221
225
|
|
|
222
|
-
The
|
|
226
|
+
The strategy system supports multiple wallet types through a wallet abstraction layer. By default, adapters use local private keys (self-custodial wallets), but you can inject custom wallet providers for custodial wallets like Privy or Turnkey.
|
|
223
227
|
|
|
224
228
|
### Default Behavior (Local Wallets)
|
|
225
229
|
|
|
226
230
|
By default, adapters use `LocalWalletProvider` which resolves private keys from:
|
|
227
231
|
- `wallets.json` (matched by address)
|
|
228
|
-
- Environment variables (`PRIVATE_KEY`, `
|
|
232
|
+
- Environment variables (`PRIVATE_KEY`, `PRIVATE_KEY_STRATEGY`)
|
|
229
233
|
- Wallet config in `config.json`
|
|
230
234
|
|
|
231
235
|
No code changes are required - existing strategies continue to work.
|
|
@@ -235,7 +239,7 @@ No code changes are required - existing strategies continue to work.
|
|
|
235
239
|
To use a custodial wallet provider (e.g., Privy, Turnkey), inject it directly into adapters:
|
|
236
240
|
|
|
237
241
|
```python
|
|
238
|
-
from
|
|
242
|
+
from adapters.evm_transaction_adapter.adapter import EvmTransactionAdapter
|
|
239
243
|
from my_privy_integration import PrivyWalletProvider
|
|
240
244
|
|
|
241
245
|
# Create your custom wallet provider
|
|
@@ -260,7 +264,7 @@ See `core/wallets/README.md` for details on implementing custom wallet providers
|
|
|
260
264
|
|
|
261
265
|
## Authentication with Wayfinder Backend
|
|
262
266
|
|
|
263
|
-
Wayfinder
|
|
267
|
+
Wayfinder Paths supports **dual authentication** for different use cases:
|
|
264
268
|
|
|
265
269
|
### 1. Service Account Authentication (API Key)
|
|
266
270
|
|
|
@@ -270,7 +274,7 @@ API keys provide service account authentication and are automatically discovered
|
|
|
270
274
|
|
|
271
275
|
#### Option A: Strategy Constructor (Programmatic)
|
|
272
276
|
```python
|
|
273
|
-
from wayfinder_paths.
|
|
277
|
+
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import StablecoinYieldStrategy
|
|
274
278
|
|
|
275
279
|
strategy = StablecoinYieldStrategy(
|
|
276
280
|
config={...},
|
|
@@ -301,10 +305,11 @@ export WAYFINDER_API_KEY="sk_live_abc123..."
|
|
|
301
305
|
- When a strategy receives an `api_key`, it sets `os.environ["WAYFINDER_API_KEY"]`
|
|
302
306
|
- All clients created by adapters automatically discover the API key from:
|
|
303
307
|
- Constructor parameter (if passed)
|
|
304
|
-
- `config.json` (via `_load_config_credentials()`)
|
|
308
|
+
- `config.json` (via `WayfinderClient._load_config_credentials()` which reads `user.api_key` or `system.api_key`)
|
|
305
309
|
- `WAYFINDER_API_KEY` environment variable
|
|
306
310
|
- API keys are included in **all** API requests (including public endpoints) for rate limiting
|
|
307
311
|
- No need to pass API keys explicitly to adapters or clients—they auto-discover it
|
|
312
|
+
- **Note:** API keys in `config.json` are loaded directly by clients, not stored in the `UserConfig` or `SystemConfig` dataclasses
|
|
308
313
|
|
|
309
314
|
### 2. Personal Access Authentication (OAuth)
|
|
310
315
|
|
|
@@ -313,7 +318,7 @@ export WAYFINDER_API_KEY="sk_live_abc123..."
|
|
|
313
318
|
The `username` and `password` in your config authenticate with the Wayfinder backend to access:
|
|
314
319
|
- Wallet management
|
|
315
320
|
- Transaction signing services
|
|
316
|
-
-
|
|
321
|
+
- Strategy execution services
|
|
317
322
|
|
|
318
323
|
```json
|
|
319
324
|
{
|
|
@@ -338,7 +343,7 @@ The `username` and `password` in your config authenticate with the Wayfinder bac
|
|
|
338
343
|
|
|
339
344
|
## Configuration in Strategies
|
|
340
345
|
|
|
341
|
-
Strategies receive configuration automatically through
|
|
346
|
+
Strategies receive configuration automatically through StrategyJob:
|
|
342
347
|
|
|
343
348
|
```python
|
|
344
349
|
from core.strategies.Strategy import Strategy
|
wayfinder_paths/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Wayfinder Path - Trading strategies and adapters for automated
|
|
1
|
+
"""Wayfinder Path - Trading strategies and adapters for automated strategy management"""
|
|
2
2
|
|
|
3
3
|
__version__ = "0.1.0"
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ from wayfinder_paths.core import (
|
|
|
8
8
|
StatusDict,
|
|
9
9
|
StatusTuple,
|
|
10
10
|
Strategy,
|
|
11
|
-
|
|
11
|
+
StrategyJob,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
@@ -17,5 +17,5 @@ __all__ = [
|
|
|
17
17
|
"Strategy",
|
|
18
18
|
"StatusDict",
|
|
19
19
|
"StatusTuple",
|
|
20
|
-
"
|
|
20
|
+
"StrategyJob",
|
|
21
21
|
]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Balance Adapter
|
|
2
2
|
|
|
3
|
-
Adapter that exposes wallet, token, and pool balances backed by `WalletClient`/`TokenClient` and now orchestrates transfers between the configured main/
|
|
3
|
+
Adapter that exposes wallet, token, and pool balances backed by `WalletClient`/`TokenClient` and now orchestrates transfers between the configured main/strategy wallets (with ledger bookkeeping).
|
|
4
4
|
|
|
5
|
-
- Entrypoint: `
|
|
5
|
+
- Entrypoint: `adapters.balance_adapter.adapter.BalanceAdapter`
|
|
6
6
|
- Manifest: `manifest.yaml`
|
|
7
7
|
- Tests: `test_adapter.py`
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ The adapter declares both `wallet_read` and `wallet_transfer` capabilities in it
|
|
|
14
14
|
|
|
15
15
|
```python
|
|
16
16
|
from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
|
|
17
|
-
from wayfinder_paths.
|
|
17
|
+
from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
|
|
18
18
|
|
|
19
19
|
web3_service = DefaultWeb3Service(config)
|
|
20
20
|
balance = BalanceAdapter(config, web3_service=web3_service)
|
|
@@ -41,7 +41,7 @@ Fetches the amount supplied to a specific pool, using the `/wallets/pool-balance
|
|
|
41
41
|
success, amount = await balance.get_pool_balance(
|
|
42
42
|
pool_address="0xPool",
|
|
43
43
|
chain_id=8453,
|
|
44
|
-
user_address=config["
|
|
44
|
+
user_address=config["strategy_wallet"]["address"],
|
|
45
45
|
)
|
|
46
46
|
```
|
|
47
47
|
|
|
@@ -50,27 +50,27 @@ Returns the enriched token balance payload Wayfinder exposes (including USD valu
|
|
|
50
50
|
|
|
51
51
|
```python
|
|
52
52
|
success, snapshot = await balance.get_all_balances(
|
|
53
|
-
wallet_address=config["
|
|
53
|
+
wallet_address=config["strategy_wallet"]["address"],
|
|
54
54
|
enrich=True,
|
|
55
55
|
)
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
### `
|
|
59
|
-
Sends the specified token from the configured `main_wallet` to the
|
|
58
|
+
### `move_from_main_wallet_to_strategy_wallet(token_id: str, amount: float, strategy_name="unknown", skip_ledger=False)`
|
|
59
|
+
Sends the specified token from the configured `main_wallet` to the strategy wallet, records the ledger deposit (unless `skip_ledger=True`), and returns the `(success, tx_result)` tuple from the underlying send helper.
|
|
60
60
|
|
|
61
61
|
```python
|
|
62
|
-
success, tx = await balance.
|
|
62
|
+
success, tx = await balance.move_from_main_wallet_to_strategy_wallet(
|
|
63
63
|
token_id="usd-coin-base",
|
|
64
64
|
amount=1.5,
|
|
65
65
|
strategy_name="MyStrategy",
|
|
66
66
|
)
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
### `
|
|
70
|
-
Mirrors the previous method but withdraws from the
|
|
69
|
+
### `move_from_strategy_wallet_to_main_wallet(token_id: str, amount: float, strategy_name="unknown", skip_ledger=False)`
|
|
70
|
+
Mirrors the previous method but withdraws from the strategy wallet back to the main wallet while recording a ledger withdrawal entry.
|
|
71
71
|
|
|
72
72
|
```python
|
|
73
|
-
await balance.
|
|
73
|
+
await balance.move_from_strategy_wallet_to_main_wallet(
|
|
74
74
|
token_id="usd-coin-base",
|
|
75
75
|
amount=0.75,
|
|
76
76
|
strategy_name="MyStrategy",
|
|
@@ -94,7 +94,7 @@ class MyStrategy(Strategy):
|
|
|
94
94
|
success, pool_balance = await self.balance_adapter.get_pool_balance(
|
|
95
95
|
pool_address=self.current_pool["address"],
|
|
96
96
|
chain_id=self.current_pool["chain"]["id"],
|
|
97
|
-
user_address=self.config["
|
|
97
|
+
user_address=self.config["strategy_wallet"]["address"],
|
|
98
98
|
)
|
|
99
99
|
return {"portfolio_value": float(pool_balance or 0), ...}
|
|
100
100
|
```
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
+
from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
4
|
+
from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
|
|
3
5
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
4
6
|
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
5
7
|
from wayfinder_paths.core.clients.WalletClient import WalletClient
|
|
8
|
+
from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
|
|
6
9
|
from wayfinder_paths.core.services.base import Web3Service
|
|
7
10
|
from wayfinder_paths.core.settings import settings
|
|
8
11
|
from wayfinder_paths.core.utils.evm_helpers import resolve_chain_id
|
|
9
|
-
from wayfinder_paths.vaults.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
10
|
-
from wayfinder_paths.vaults.adapters.token_adapter.adapter import TokenAdapter
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class BalanceAdapter(BaseAdapter):
|
|
@@ -44,7 +45,7 @@ class BalanceAdapter(BaseAdapter):
|
|
|
44
45
|
*,
|
|
45
46
|
token_id: str,
|
|
46
47
|
wallet_address: str,
|
|
47
|
-
) -> tuple[bool,
|
|
48
|
+
) -> tuple[bool, str | int]:
|
|
48
49
|
"""Get token balance for a wallet."""
|
|
49
50
|
try:
|
|
50
51
|
data = await self.wallet_client.get_token_balance_for_wallet(
|
|
@@ -55,37 +56,37 @@ class BalanceAdapter(BaseAdapter):
|
|
|
55
56
|
except Exception as e:
|
|
56
57
|
return (False, str(e))
|
|
57
58
|
|
|
58
|
-
async def
|
|
59
|
+
async def move_from_main_wallet_to_strategy_wallet(
|
|
59
60
|
self,
|
|
60
61
|
token_id: str,
|
|
61
62
|
amount: float,
|
|
62
63
|
strategy_name: str = "unknown",
|
|
63
64
|
skip_ledger: bool = False,
|
|
64
65
|
) -> tuple[bool, Any]:
|
|
65
|
-
"""Move funds from the configured main wallet into the
|
|
66
|
+
"""Move funds from the configured main wallet into the strategy wallet."""
|
|
66
67
|
return await self._move_between_wallets(
|
|
67
68
|
token_id=token_id,
|
|
68
69
|
amount=amount,
|
|
69
70
|
from_wallet=self.config.get("main_wallet"),
|
|
70
|
-
to_wallet=self.config.get("
|
|
71
|
+
to_wallet=self.config.get("strategy_wallet"),
|
|
71
72
|
ledger_method=self.ledger_adapter.record_deposit,
|
|
72
73
|
ledger_wallet="to",
|
|
73
74
|
strategy_name=strategy_name,
|
|
74
75
|
skip_ledger=skip_ledger,
|
|
75
76
|
)
|
|
76
77
|
|
|
77
|
-
async def
|
|
78
|
+
async def move_from_strategy_wallet_to_main_wallet(
|
|
78
79
|
self,
|
|
79
80
|
token_id: str,
|
|
80
81
|
amount: float,
|
|
81
82
|
strategy_name: str = "unknown",
|
|
82
83
|
skip_ledger: bool = False,
|
|
83
84
|
) -> tuple[bool, Any]:
|
|
84
|
-
"""Move funds from the
|
|
85
|
+
"""Move funds from the strategy wallet back into the main wallet."""
|
|
85
86
|
return await self._move_between_wallets(
|
|
86
87
|
token_id=token_id,
|
|
87
88
|
amount=amount,
|
|
88
|
-
from_wallet=self.config.get("
|
|
89
|
+
from_wallet=self.config.get("strategy_wallet"),
|
|
89
90
|
to_wallet=self.config.get("main_wallet"),
|
|
90
91
|
ledger_method=self.ledger_adapter.record_withdrawal,
|
|
91
92
|
ledger_wallet="from",
|
|
@@ -111,7 +112,7 @@ class BalanceAdapter(BaseAdapter):
|
|
|
111
112
|
from_address = self._wallet_address(from_wallet)
|
|
112
113
|
to_address = self._wallet_address(to_wallet)
|
|
113
114
|
if not from_address or not to_address:
|
|
114
|
-
return False, "main_wallet or
|
|
115
|
+
return False, "main_wallet or strategy_wallet missing"
|
|
115
116
|
|
|
116
117
|
token_info = await self.token_client.get_token_details(token_id)
|
|
117
118
|
if not token_info:
|
|
@@ -132,7 +133,7 @@ class BalanceAdapter(BaseAdapter):
|
|
|
132
133
|
broadcast_result = (True, {"dry_run": True, "transaction": tx})
|
|
133
134
|
else:
|
|
134
135
|
broadcast_result = await self.wallet_provider.broadcast_transaction(
|
|
135
|
-
tx, wait_for_receipt=True, timeout=
|
|
136
|
+
tx, wait_for_receipt=True, timeout=DEFAULT_TRANSACTION_TIMEOUT
|
|
136
137
|
)
|
|
137
138
|
|
|
138
139
|
if broadcast_result[0] and not skip_ledger and ledger_method is not None:
|
|
@@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, patch
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from wayfinder_paths.
|
|
5
|
+
from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TestBalanceAdapter:
|
|
@@ -21,19 +21,25 @@ class TestBalanceAdapter:
|
|
|
21
21
|
return mock_client
|
|
22
22
|
|
|
23
23
|
@pytest.fixture
|
|
24
|
-
def
|
|
24
|
+
def mock_web3_service(self):
|
|
25
|
+
"""Mock TokenClient for testing"""
|
|
26
|
+
mock_client = AsyncMock()
|
|
27
|
+
return mock_client
|
|
28
|
+
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def adapter(self, mock_wallet_client, mock_token_client, mock_web3_service):
|
|
25
31
|
"""Create a BalanceAdapter instance with mocked clients for testing"""
|
|
26
32
|
with (
|
|
27
33
|
patch(
|
|
28
|
-
"wayfinder_paths.
|
|
34
|
+
"wayfinder_paths.adapters.balance_adapter.adapter.WalletClient",
|
|
29
35
|
return_value=mock_wallet_client,
|
|
30
36
|
),
|
|
31
37
|
patch(
|
|
32
|
-
"wayfinder_paths.
|
|
38
|
+
"wayfinder_paths.adapters.balance_adapter.adapter.TokenClient",
|
|
33
39
|
return_value=mock_token_client,
|
|
34
40
|
),
|
|
35
41
|
):
|
|
36
|
-
return BalanceAdapter(config={})
|
|
42
|
+
return BalanceAdapter(config={}, web3_service=mock_web3_service)
|
|
37
43
|
|
|
38
44
|
@pytest.mark.asyncio
|
|
39
45
|
async def test_health_check(self, adapter):
|
|
@@ -68,7 +74,7 @@ class TestBalanceAdapter:
|
|
|
68
74
|
return_value=(True, mock_response)
|
|
69
75
|
)
|
|
70
76
|
|
|
71
|
-
success, data = await adapter.
|
|
77
|
+
success, data = await adapter.get_all_balances(
|
|
72
78
|
wallet_address="0x1234567890123456789012345678901234567890",
|
|
73
79
|
enrich=True,
|
|
74
80
|
)
|
|
@@ -26,7 +26,7 @@ The BRAPClient will automatically:
|
|
|
26
26
|
|
|
27
27
|
```python
|
|
28
28
|
from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
|
|
29
|
-
from wayfinder_paths.
|
|
29
|
+
from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
|
|
30
30
|
|
|
31
31
|
web3_service = DefaultWeb3Service(config)
|
|
32
32
|
adapter = BRAPAdapter(web3_service=web3_service)
|
|
@@ -240,7 +240,7 @@ All methods return a tuple of `(success: bool, data: Any)` where:
|
|
|
240
240
|
Run the adapter tests:
|
|
241
241
|
|
|
242
242
|
```bash
|
|
243
|
-
pytest wayfinder_paths/
|
|
243
|
+
pytest wayfinder_paths/adapters/brap_adapter/test_adapter.py -v
|
|
244
244
|
```
|
|
245
245
|
|
|
246
246
|
## Dependencies
|
|
@@ -1,16 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from typing import Any
|
|
2
4
|
|
|
3
5
|
from eth_utils import to_checksum_address
|
|
4
6
|
|
|
7
|
+
from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
8
|
+
from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
|
|
5
9
|
from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
|
|
6
|
-
from wayfinder_paths.core.
|
|
7
|
-
from wayfinder_paths.core.clients.
|
|
10
|
+
from wayfinder_paths.core.adapters.models import SWAP
|
|
11
|
+
from wayfinder_paths.core.clients.BRAPClient import BRAPClient, BRAPQuote
|
|
12
|
+
from wayfinder_paths.core.clients.LedgerClient import TransactionRecord
|
|
13
|
+
from wayfinder_paths.core.clients.SimulationClient import (
|
|
14
|
+
SimulationClient,
|
|
15
|
+
SimulationResult,
|
|
16
|
+
)
|
|
8
17
|
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
9
18
|
from wayfinder_paths.core.constants import DEFAULT_SLIPPAGE
|
|
19
|
+
from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
|
|
10
20
|
from wayfinder_paths.core.services.base import Web3Service
|
|
11
21
|
from wayfinder_paths.core.settings import settings
|
|
12
|
-
from wayfinder_paths.vaults.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
13
|
-
from wayfinder_paths.vaults.adapters.token_adapter.adapter import TokenAdapter
|
|
14
22
|
|
|
15
23
|
_NEEDS_CLEAR_APPROVAL = {
|
|
16
24
|
(1, "0xdac17f958d2ee523a2206206994597c13d831ec7"),
|
|
@@ -61,7 +69,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
61
69
|
amount: str,
|
|
62
70
|
slippage: float | None = None,
|
|
63
71
|
wayfinder_fee: float | None = None,
|
|
64
|
-
) -> tuple[bool,
|
|
72
|
+
) -> tuple[bool, BRAPQuote | str]:
|
|
65
73
|
"""
|
|
66
74
|
Get a quote for a cross-chain swap operation.
|
|
67
75
|
|
|
@@ -107,7 +115,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
107
115
|
amount: str,
|
|
108
116
|
slippage: float | None = None,
|
|
109
117
|
wayfinder_fee: float | None = None,
|
|
110
|
-
) -> tuple[bool, Any]:
|
|
118
|
+
) -> tuple[bool, dict[str, Any] | str]:
|
|
111
119
|
"""
|
|
112
120
|
Get the best available quote for a swap operation.
|
|
113
121
|
|
|
@@ -597,7 +605,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
597
605
|
return await self.wallet_provider.broadcast_transaction(
|
|
598
606
|
transaction,
|
|
599
607
|
wait_for_receipt=True,
|
|
600
|
-
timeout=
|
|
608
|
+
timeout=DEFAULT_TRANSACTION_TIMEOUT,
|
|
601
609
|
)
|
|
602
610
|
|
|
603
611
|
async def _record_swap_operation(
|
|
@@ -608,7 +616,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
608
616
|
quote: dict[str, Any],
|
|
609
617
|
broadcast_response: dict[str, Any] | Any,
|
|
610
618
|
strategy_name: str | None = None,
|
|
611
|
-
) -> Any:
|
|
619
|
+
) -> TransactionRecord | dict[str, Any]:
|
|
612
620
|
from_amount_usd = quote.get("from_amount_usd")
|
|
613
621
|
if from_amount_usd is None:
|
|
614
622
|
from_amount_usd = await self._token_amount_usd(
|
|
@@ -622,23 +630,22 @@ class BRAPAdapter(BaseAdapter):
|
|
|
622
630
|
)
|
|
623
631
|
|
|
624
632
|
response = broadcast_response if isinstance(broadcast_response, dict) else {}
|
|
625
|
-
|
|
626
|
-
"
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
"
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
633
|
+
operation_data = SWAP(
|
|
634
|
+
from_token_id=from_token.get("id"),
|
|
635
|
+
to_token_id=to_token.get("id"),
|
|
636
|
+
from_amount=quote.get("input_amount"),
|
|
637
|
+
to_amount=quote.get("output_amount"),
|
|
638
|
+
from_amount_usd=from_amount_usd or 0,
|
|
639
|
+
to_amount_usd=to_amount_usd or 0,
|
|
640
|
+
transaction_hash=response.get("transaction_hash"),
|
|
641
|
+
transaction_status=response.get("transaction_status"),
|
|
642
|
+
transaction_receipt=response.get("transaction_receipt"),
|
|
643
|
+
)
|
|
637
644
|
|
|
638
645
|
try:
|
|
639
646
|
success, ledger_response = await self.ledger_adapter.record_operation(
|
|
640
647
|
wallet_address=wallet_address,
|
|
641
|
-
operation_data=
|
|
648
|
+
operation_data=operation_data,
|
|
642
649
|
usd_value=from_amount_usd or 0,
|
|
643
650
|
strategy_name=strategy_name,
|
|
644
651
|
)
|
|
@@ -650,7 +657,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
650
657
|
except Exception as exc: # noqa: BLE001
|
|
651
658
|
self.logger.warning(f"Ledger swap record raised: {exc}", quote=quote)
|
|
652
659
|
|
|
653
|
-
return
|
|
660
|
+
return operation_data.model_dump(mode="json")
|
|
654
661
|
|
|
655
662
|
async def _token_amount_usd(
|
|
656
663
|
self, token_info: dict[str, Any], raw_amount: Any
|
|
@@ -676,7 +683,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
676
683
|
from_address: str,
|
|
677
684
|
chain_id: int,
|
|
678
685
|
quote: dict[str, Any],
|
|
679
|
-
) ->
|
|
686
|
+
) -> SimulationResult:
|
|
680
687
|
client = await self._get_simulation_client()
|
|
681
688
|
initial_balances = {"native": "5000000000000000000"}
|
|
682
689
|
if from_token.get("address"):
|
|
@@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, patch
|
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
-
from wayfinder_paths.
|
|
6
|
+
from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestBRAPAdapter:
|
|
@@ -34,7 +34,7 @@ class TestBRAPAdapter:
|
|
|
34
34
|
def adapter(self, mock_brap_client, mock_web3_service):
|
|
35
35
|
"""Create a BRAPAdapter instance with mocked client for testing"""
|
|
36
36
|
with patch(
|
|
37
|
-
"
|
|
37
|
+
"adapters.brap_adapter.adapter.BRAPClient",
|
|
38
38
|
return_value=mock_brap_client,
|
|
39
39
|
):
|
|
40
40
|
return BRAPAdapter(web3_service=mock_web3_service)
|