wayfinder-paths 0.1.13__tar.gz → 0.1.14__tar.gz
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-0.1.13 → wayfinder_paths-0.1.14}/PKG-INFO +14 -49
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/README.md +13 -48
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/pyproject.toml +1 -1
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/README.md +13 -14
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
- wayfinder_paths-0.1.14/wayfinder_paths/adapters/balance_adapter/test_adapter.py +182 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/README.md +11 -16
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/README.md +9 -10
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/README.md +2 -14
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
- wayfinder_paths-0.1.14/wayfinder_paths/adapters/token_adapter/examples.json +22 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
- wayfinder_paths-0.1.14/wayfinder_paths/core/clients/BRAPClient.py +151 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/ClientManager.py +1 -68
- wayfinder_paths-0.1.14/wayfinder_paths/core/clients/HyperlendClient.py +253 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/LedgerClient.py +1 -4
- wayfinder_paths-0.1.14/wayfinder_paths/core/clients/PoolClient.py +150 -0
- wayfinder_paths-0.1.14/wayfinder_paths/core/clients/TokenClient.py +144 -0
- wayfinder_paths-0.1.14/wayfinder_paths/core/clients/WalletClient.py +56 -0
- wayfinder_paths-0.1.14/wayfinder_paths/core/clients/WayfinderClient.py +131 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/__init__.py +0 -2
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/protocols.py +35 -46
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/sdk_example.py +37 -22
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/engine/StrategyJob.py +7 -55
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/local_evm_txn.py +6 -6
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/local_token_txn.py +1 -1
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/Strategy.py +0 -2
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/utils/evm_helpers.py +2 -2
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/run_strategy.py +8 -19
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/README.md +3 -3
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/test_strategy.py +3 -2
- wayfinder_paths-0.1.13/wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -59
- wayfinder_paths-0.1.13/wayfinder_paths/adapters/token_adapter/examples.json +0 -26
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/AuthClient.py +0 -83
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/BRAPClient.py +0 -110
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/HyperlendClient.py +0 -192
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/PoolClient.py +0 -76
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/TokenClient.py +0 -89
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/WalletClient.py +0 -86
- wayfinder_paths-0.1.13/wayfinder_paths/core/clients/WayfinderClient.py +0 -263
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/LICENSE +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/abis/generic/erc20.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/executor.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/conftest.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/BaseAdapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/base.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/models.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/bootstrap.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/stats.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/test_analytics.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/config.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/base.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/erc20_abi.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/hyperlend_abi.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/moonwell_abi.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/engine/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/base.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/web3_service.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/base.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/descriptors.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/utils/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/utils/wallets.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/wallets/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/wallets/WalletManager.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/wallets/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/enso.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/erc20.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/evm.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/hyper_evm.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/hyperlend.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/hyperliquid.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/moonwell.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/prjx.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/util.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/create_strategy.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/make_wallets.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/run_strategy.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/constants.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/config.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/README.md +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/test_adapter.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/examples.json +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/strategy.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/__init__.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/test_smoke_manifest.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/test_test_coverage.py +0 -0
- {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wayfinder-paths
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.14
|
|
4
4
|
Summary: Wayfinder Path: strategies and adapters
|
|
5
5
|
Author: Wayfinder
|
|
6
6
|
Author-email: dev@wayfinder.ai
|
|
@@ -216,63 +216,28 @@ See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for details.
|
|
|
216
216
|
|
|
217
217
|
### Authentication
|
|
218
218
|
|
|
219
|
-
Wayfinder Paths
|
|
219
|
+
Wayfinder Paths uses API key authentication via the `X-API-KEY` header.
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
For backend services and automated systems with higher rate limits:
|
|
224
|
-
|
|
225
|
-
**Option A: Pass to Strategy Constructor**
|
|
226
|
-
|
|
227
|
-
```python
|
|
228
|
-
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import StablecoinYieldStrategy
|
|
229
|
-
|
|
230
|
-
strategy = StablecoinYieldStrategy(
|
|
231
|
-
config={...},
|
|
232
|
-
api_key="sk_live_abc123..." # API key is auto-discovered by all clients
|
|
233
|
-
)
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
**Option B: Add to config.json**
|
|
221
|
+
**Add API key to config.json:**
|
|
237
222
|
|
|
238
223
|
```json
|
|
239
224
|
{
|
|
240
|
-
"user": {
|
|
241
|
-
"api_key": "sk_live_abc123..."
|
|
242
|
-
},
|
|
243
225
|
"system": {
|
|
244
|
-
"api_key": "sk_live_abc123..."
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
**Priority Order:** Constructor parameter > `config.json` (user.api_key or system.api_key)
|
|
250
|
-
|
|
251
|
-
**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.
|
|
252
|
-
|
|
253
|
-
#### 2. Personal Access Authentication (OAuth)
|
|
254
|
-
|
|
255
|
-
For standalone SDK users with username/password:
|
|
256
|
-
|
|
257
|
-
```json
|
|
258
|
-
{
|
|
259
|
-
"user": {
|
|
260
|
-
"username": "your_username",
|
|
261
|
-
"password": "your_password",
|
|
262
|
-
"refresh_token": null // Optional: use refresh token instead
|
|
226
|
+
"api_key": "sk_live_abc123...",
|
|
227
|
+
"api_base_url": "https://wayfinder.ai/api/v1",
|
|
228
|
+
"wallets_path": "wallets.json"
|
|
263
229
|
}
|
|
264
230
|
}
|
|
265
231
|
```
|
|
266
232
|
|
|
267
233
|
**How It Works:**
|
|
268
234
|
|
|
269
|
-
- API
|
|
270
|
-
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
- 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
|
|
235
|
+
- API key is automatically loaded from `system.api_key` in config.json
|
|
236
|
+
- The API key is sent as the `X-API-KEY` header on all API requests
|
|
237
|
+
- All clients automatically include the API key header
|
|
238
|
+
- No need to pass API keys explicitly to strategies or clients
|
|
274
239
|
|
|
275
|
-
See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed
|
|
240
|
+
See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed configuration documentation.
|
|
276
241
|
|
|
277
242
|
## 🔌 Creating Adapters
|
|
278
243
|
|
|
@@ -299,7 +264,7 @@ class MyAdapter(BaseAdapter):
|
|
|
299
264
|
|
|
300
265
|
async def get_pools(self, pool_ids: list[str]):
|
|
301
266
|
data = await self.pool_client.get_pools_by_ids(
|
|
302
|
-
pool_ids=
|
|
267
|
+
pool_ids=pool_ids
|
|
303
268
|
)
|
|
304
269
|
return (True, data)
|
|
305
270
|
```
|
|
@@ -340,7 +305,7 @@ class MyStrategy(Strategy):
|
|
|
340
305
|
return (False, "Nothing to deposit")
|
|
341
306
|
|
|
342
307
|
success, _ = await self.balance_adapter.get_balance(
|
|
343
|
-
|
|
308
|
+
query=self.config.get("token_id"),
|
|
344
309
|
wallet_address=self.config.get("main_wallet", {}).get("address"),
|
|
345
310
|
)
|
|
346
311
|
if not success:
|
|
@@ -357,7 +322,7 @@ class MyStrategy(Strategy):
|
|
|
357
322
|
async def _status(self) -> StatusDict:
|
|
358
323
|
"""Report balances back to the runner"""
|
|
359
324
|
success, balance = await self.balance_adapter.get_balance(
|
|
360
|
-
|
|
325
|
+
query=self.config.get("token_id"),
|
|
361
326
|
wallet_address=self.config.get("strategy_wallet", {}).get("address"),
|
|
362
327
|
)
|
|
363
328
|
return {
|
|
@@ -195,63 +195,28 @@ See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for details.
|
|
|
195
195
|
|
|
196
196
|
### Authentication
|
|
197
197
|
|
|
198
|
-
Wayfinder Paths
|
|
198
|
+
Wayfinder Paths uses API key authentication via the `X-API-KEY` header.
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
For backend services and automated systems with higher rate limits:
|
|
203
|
-
|
|
204
|
-
**Option A: Pass to Strategy Constructor**
|
|
205
|
-
|
|
206
|
-
```python
|
|
207
|
-
from wayfinder_paths.strategies.stablecoin_yield_strategy.strategy import StablecoinYieldStrategy
|
|
208
|
-
|
|
209
|
-
strategy = StablecoinYieldStrategy(
|
|
210
|
-
config={...},
|
|
211
|
-
api_key="sk_live_abc123..." # API key is auto-discovered by all clients
|
|
212
|
-
)
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**Option B: Add to config.json**
|
|
200
|
+
**Add API key to config.json:**
|
|
216
201
|
|
|
217
202
|
```json
|
|
218
203
|
{
|
|
219
|
-
"user": {
|
|
220
|
-
"api_key": "sk_live_abc123..."
|
|
221
|
-
},
|
|
222
204
|
"system": {
|
|
223
|
-
"api_key": "sk_live_abc123..."
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
**Priority Order:** Constructor parameter > `config.json` (user.api_key or system.api_key)
|
|
229
|
-
|
|
230
|
-
**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.
|
|
231
|
-
|
|
232
|
-
#### 2. Personal Access Authentication (OAuth)
|
|
233
|
-
|
|
234
|
-
For standalone SDK users with username/password:
|
|
235
|
-
|
|
236
|
-
```json
|
|
237
|
-
{
|
|
238
|
-
"user": {
|
|
239
|
-
"username": "your_username",
|
|
240
|
-
"password": "your_password",
|
|
241
|
-
"refresh_token": null // Optional: use refresh token instead
|
|
205
|
+
"api_key": "sk_live_abc123...",
|
|
206
|
+
"api_base_url": "https://wayfinder.ai/api/v1",
|
|
207
|
+
"wallets_path": "wallets.json"
|
|
242
208
|
}
|
|
243
209
|
}
|
|
244
210
|
```
|
|
245
211
|
|
|
246
212
|
**How It Works:**
|
|
247
213
|
|
|
248
|
-
- API
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
252
|
-
- 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
|
|
214
|
+
- API key is automatically loaded from `system.api_key` in config.json
|
|
215
|
+
- The API key is sent as the `X-API-KEY` header on all API requests
|
|
216
|
+
- All clients automatically include the API key header
|
|
217
|
+
- No need to pass API keys explicitly to strategies or clients
|
|
253
218
|
|
|
254
|
-
See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed
|
|
219
|
+
See [CONFIG_GUIDE.md](wayfinder_paths/CONFIG_GUIDE.md) for detailed configuration documentation.
|
|
255
220
|
|
|
256
221
|
## 🔌 Creating Adapters
|
|
257
222
|
|
|
@@ -278,7 +243,7 @@ class MyAdapter(BaseAdapter):
|
|
|
278
243
|
|
|
279
244
|
async def get_pools(self, pool_ids: list[str]):
|
|
280
245
|
data = await self.pool_client.get_pools_by_ids(
|
|
281
|
-
pool_ids=
|
|
246
|
+
pool_ids=pool_ids
|
|
282
247
|
)
|
|
283
248
|
return (True, data)
|
|
284
249
|
```
|
|
@@ -319,7 +284,7 @@ class MyStrategy(Strategy):
|
|
|
319
284
|
return (False, "Nothing to deposit")
|
|
320
285
|
|
|
321
286
|
success, _ = await self.balance_adapter.get_balance(
|
|
322
|
-
|
|
287
|
+
query=self.config.get("token_id"),
|
|
323
288
|
wallet_address=self.config.get("main_wallet", {}).get("address"),
|
|
324
289
|
)
|
|
325
290
|
if not success:
|
|
@@ -336,7 +301,7 @@ class MyStrategy(Strategy):
|
|
|
336
301
|
async def _status(self) -> StatusDict:
|
|
337
302
|
"""Report balances back to the runner"""
|
|
338
303
|
success, balance = await self.balance_adapter.get_balance(
|
|
339
|
-
|
|
304
|
+
query=self.config.get("token_id"),
|
|
340
305
|
wallet_address=self.config.get("strategy_wallet", {}).get("address"),
|
|
341
306
|
)
|
|
342
307
|
return {
|
{wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/README.md
RENAMED
|
@@ -23,24 +23,23 @@ balance = BalanceAdapter(config, web3_service=web3_service)
|
|
|
23
23
|
|
|
24
24
|
## API surface
|
|
25
25
|
|
|
26
|
-
### `get_balance(
|
|
27
|
-
Returns the raw balance (as an integer) for a specific token on a wallet.
|
|
26
|
+
### `get_balance(*, query: str | dict, wallet_address: str, chain_id: int | None = None)`
|
|
27
|
+
Returns the raw balance (as an integer) for a specific token or pool on a wallet.
|
|
28
|
+
|
|
29
|
+
`query`: token_id/address string or a dict with a `"token_id"` key. When `query` is a token identifier (e.g. `"usd-coin-base"`), `chain_id` is auto-resolved from token info; when it is a pool address, `chain_id` must be provided.
|
|
28
30
|
|
|
29
31
|
```python
|
|
32
|
+
# Token balance (chain_id auto-resolved)
|
|
30
33
|
success, balance = await balance.get_balance(
|
|
31
|
-
|
|
34
|
+
query="usd-coin-base",
|
|
32
35
|
wallet_address=config["main_wallet"]["address"],
|
|
33
36
|
)
|
|
34
|
-
```
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
success, amount = await balance.get_pool_balance(
|
|
41
|
-
pool_address="0xPool",
|
|
38
|
+
# Pool balance (chain_id required)
|
|
39
|
+
success, pool_balance = await balance.get_balance(
|
|
40
|
+
query="0xPool...",
|
|
41
|
+
wallet_address=config["strategy_wallet"]["address"],
|
|
42
42
|
chain_id=8453,
|
|
43
|
-
user_address=config["strategy_wallet"]["address"],
|
|
44
43
|
)
|
|
45
44
|
```
|
|
46
45
|
|
|
@@ -80,10 +79,10 @@ class MyStrategy(Strategy):
|
|
|
80
79
|
self.balance_adapter = balance_adapter
|
|
81
80
|
|
|
82
81
|
async def _status(self):
|
|
83
|
-
success, pool_balance = await self.balance_adapter.
|
|
84
|
-
|
|
82
|
+
success, pool_balance = await self.balance_adapter.get_balance(
|
|
83
|
+
query=self.current_pool["address"],
|
|
84
|
+
wallet_address=self.config["strategy_wallet"]["address"],
|
|
85
85
|
chain_id=self.current_pool["chain"]["id"],
|
|
86
|
-
user_address=self.config["strategy_wallet"]["address"],
|
|
87
86
|
)
|
|
88
87
|
return {"portfolio_value": float(pool_balance or 0), ...}
|
|
89
88
|
```
|
|
@@ -42,16 +42,39 @@ class BalanceAdapter(BaseAdapter):
|
|
|
42
42
|
async def get_balance(
|
|
43
43
|
self,
|
|
44
44
|
*,
|
|
45
|
-
|
|
45
|
+
query: str | dict[str, Any],
|
|
46
46
|
wallet_address: str,
|
|
47
|
+
chain_id: int | None = None,
|
|
47
48
|
) -> tuple[bool, str | int]:
|
|
48
|
-
"""Get token balance for a wallet.
|
|
49
|
+
"""Get token or pool balance for a wallet.
|
|
50
|
+
|
|
51
|
+
query: token_id/address string or a dict with a "token_id" key.
|
|
52
|
+
"""
|
|
53
|
+
resolved = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
54
|
+
if not resolved:
|
|
55
|
+
return (False, "missing query")
|
|
49
56
|
try:
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
if chain_id is None:
|
|
58
|
+
token_info = await self.token_client.get_token_details(resolved)
|
|
59
|
+
if not token_info:
|
|
60
|
+
return (False, f"Token not found: {resolved}")
|
|
61
|
+
resolved_chain_id = resolve_chain_id(token_info, self.logger)
|
|
62
|
+
if resolved_chain_id is None:
|
|
63
|
+
return (False, f"Token {resolved} is missing a chain id")
|
|
64
|
+
chain_id = resolved_chain_id
|
|
65
|
+
|
|
66
|
+
data = await self.wallet_client.get_token_balance_for_address(
|
|
52
67
|
wallet_address=wallet_address,
|
|
68
|
+
query=resolved,
|
|
69
|
+
chain_id=int(chain_id),
|
|
53
70
|
)
|
|
54
|
-
|
|
71
|
+
# Use _parse_balance for consistent parsing (handles balance_raw or balance)
|
|
72
|
+
raw = (
|
|
73
|
+
data.get("balance_raw") or data.get("balance")
|
|
74
|
+
if isinstance(data, dict)
|
|
75
|
+
else None
|
|
76
|
+
)
|
|
77
|
+
return (True, self._parse_balance(raw))
|
|
55
78
|
except Exception as e:
|
|
56
79
|
return (False, str(e))
|
|
57
80
|
|
|
@@ -159,6 +182,7 @@ class BalanceAdapter(BaseAdapter):
|
|
|
159
182
|
|
|
160
183
|
usd_value = await self._token_amount_usd(token_info, amount)
|
|
161
184
|
try:
|
|
185
|
+
token_id = token_info.get("token_id") or token_info.get("id")
|
|
162
186
|
success, response = await ledger_method(
|
|
163
187
|
wallet_address=wallet_address,
|
|
164
188
|
chain_id=chain_id,
|
|
@@ -166,7 +190,7 @@ class BalanceAdapter(BaseAdapter):
|
|
|
166
190
|
token_amount=str(amount),
|
|
167
191
|
usd_value=usd_value,
|
|
168
192
|
data={
|
|
169
|
-
"token_id":
|
|
193
|
+
"token_id": token_id,
|
|
170
194
|
"amount": str(amount),
|
|
171
195
|
"usd_value": usd_value,
|
|
172
196
|
},
|
|
@@ -176,15 +200,16 @@ class BalanceAdapter(BaseAdapter):
|
|
|
176
200
|
self.logger.warning(
|
|
177
201
|
"Ledger entry failed",
|
|
178
202
|
wallet=wallet_address,
|
|
179
|
-
token_id=
|
|
203
|
+
token_id=token_id,
|
|
180
204
|
amount=amount,
|
|
181
205
|
error=response,
|
|
182
206
|
)
|
|
183
207
|
except Exception as exc: # noqa: BLE001
|
|
208
|
+
token_id = token_info.get("token_id") or token_info.get("id")
|
|
184
209
|
self.logger.warning(
|
|
185
210
|
f"Ledger entry raised: {exc}",
|
|
186
211
|
wallet=wallet_address,
|
|
187
|
-
token_id=
|
|
212
|
+
token_id=token_id,
|
|
188
213
|
)
|
|
189
214
|
|
|
190
215
|
async def _token_amount_usd(
|
|
@@ -208,27 +233,3 @@ class BalanceAdapter(BaseAdapter):
|
|
|
208
233
|
if isinstance(evm_wallet, dict):
|
|
209
234
|
return evm_wallet.get("address")
|
|
210
235
|
return None
|
|
211
|
-
|
|
212
|
-
async def get_pool_balance(
|
|
213
|
-
self,
|
|
214
|
-
*,
|
|
215
|
-
pool_address: str,
|
|
216
|
-
chain_id: int,
|
|
217
|
-
user_address: str,
|
|
218
|
-
) -> tuple[bool, Any]:
|
|
219
|
-
"""Get pool balance for a wallet."""
|
|
220
|
-
try:
|
|
221
|
-
data = await self.wallet_client.get_pool_balance_for_wallet(
|
|
222
|
-
pool_address=pool_address,
|
|
223
|
-
chain_id=chain_id,
|
|
224
|
-
user_address=user_address,
|
|
225
|
-
human_readable=False,
|
|
226
|
-
)
|
|
227
|
-
raw = (
|
|
228
|
-
data.get("balance_raw") or data.get("balance")
|
|
229
|
-
if isinstance(data, dict)
|
|
230
|
-
else None
|
|
231
|
-
)
|
|
232
|
-
return (True, self._parse_balance(raw))
|
|
233
|
-
except Exception as e:
|
|
234
|
-
return (False, str(e))
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from unittest.mock import AsyncMock, patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestBalanceAdapter:
|
|
9
|
+
"""Test cases for BalanceAdapter"""
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def mock_wallet_client(self):
|
|
13
|
+
"""Mock WalletClient for testing"""
|
|
14
|
+
mock_client = AsyncMock()
|
|
15
|
+
return mock_client
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def mock_token_client(self):
|
|
19
|
+
"""Mock TokenClient for testing"""
|
|
20
|
+
mock_client = AsyncMock()
|
|
21
|
+
return mock_client
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
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):
|
|
31
|
+
"""Create a BalanceAdapter instance with mocked clients for testing"""
|
|
32
|
+
with (
|
|
33
|
+
patch(
|
|
34
|
+
"wayfinder_paths.adapters.balance_adapter.adapter.WalletClient",
|
|
35
|
+
return_value=mock_wallet_client,
|
|
36
|
+
),
|
|
37
|
+
patch(
|
|
38
|
+
"wayfinder_paths.adapters.balance_adapter.adapter.TokenClient",
|
|
39
|
+
return_value=mock_token_client,
|
|
40
|
+
),
|
|
41
|
+
):
|
|
42
|
+
return BalanceAdapter(config={}, web3_service=mock_web3_service)
|
|
43
|
+
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_health_check(self, adapter):
|
|
46
|
+
"""Test adapter health check"""
|
|
47
|
+
health = await adapter.health_check()
|
|
48
|
+
assert isinstance(health, dict)
|
|
49
|
+
assert health.get("status") in {"healthy", "unhealthy", "error"}
|
|
50
|
+
|
|
51
|
+
@pytest.mark.asyncio
|
|
52
|
+
async def test_connect(self, adapter):
|
|
53
|
+
"""Test adapter connection"""
|
|
54
|
+
ok = await adapter.connect()
|
|
55
|
+
assert isinstance(ok, bool)
|
|
56
|
+
|
|
57
|
+
def test_adapter_type(self, adapter):
|
|
58
|
+
"""Test adapter has adapter_type"""
|
|
59
|
+
assert adapter.adapter_type == "BALANCE"
|
|
60
|
+
|
|
61
|
+
@pytest.mark.asyncio
|
|
62
|
+
async def test_get_balance_with_query_string(
|
|
63
|
+
self, adapter, mock_token_client, mock_wallet_client
|
|
64
|
+
):
|
|
65
|
+
"""Test get_balance with query as string (auto-resolves chain_id)."""
|
|
66
|
+
mock_token_client.get_token_details = AsyncMock(
|
|
67
|
+
return_value={
|
|
68
|
+
"token_id": "usd-coin-base",
|
|
69
|
+
"address": "0x123",
|
|
70
|
+
"chain": {"id": 8453, "code": "base"},
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
mock_wallet_client.get_token_balance_for_address = AsyncMock(
|
|
74
|
+
return_value={"balance": 1000000}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
success, balance = await adapter.get_balance(
|
|
78
|
+
query="usd-coin-base",
|
|
79
|
+
wallet_address="0xWallet",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
assert success is True
|
|
83
|
+
assert balance == 1000000
|
|
84
|
+
mock_token_client.get_token_details.assert_called_once_with("usd-coin-base")
|
|
85
|
+
mock_wallet_client.get_token_balance_for_address.assert_called_once_with(
|
|
86
|
+
wallet_address="0xWallet",
|
|
87
|
+
query="usd-coin-base",
|
|
88
|
+
chain_id=8453,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
@pytest.mark.asyncio
|
|
92
|
+
async def test_get_balance_with_query_dict(
|
|
93
|
+
self, adapter, mock_token_client, mock_wallet_client
|
|
94
|
+
):
|
|
95
|
+
"""get_balance accepts query= as dict with token_id key."""
|
|
96
|
+
mock_token_client.get_token_details = AsyncMock(
|
|
97
|
+
return_value={
|
|
98
|
+
"token_id": "wsteth-base",
|
|
99
|
+
"address": "0x456",
|
|
100
|
+
"chain": {"id": 8453, "code": "base"},
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
mock_wallet_client.get_token_balance_for_address = AsyncMock(
|
|
104
|
+
return_value={"balance": 3000000}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
success, balance = await adapter.get_balance(
|
|
108
|
+
query={"token_id": "wsteth-base"},
|
|
109
|
+
wallet_address="0x123",
|
|
110
|
+
)
|
|
111
|
+
assert success is True
|
|
112
|
+
assert balance == 3000000
|
|
113
|
+
mock_token_client.get_token_details.assert_called_once_with("wsteth-base")
|
|
114
|
+
mock_wallet_client.get_token_balance_for_address.assert_called_once_with(
|
|
115
|
+
wallet_address="0x123",
|
|
116
|
+
query="wsteth-base",
|
|
117
|
+
chain_id=8453,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@pytest.mark.asyncio
|
|
121
|
+
async def test_get_balance_missing_query(self, adapter):
|
|
122
|
+
"""get_balance returns error when query is empty or missing token_id."""
|
|
123
|
+
success, result = await adapter.get_balance(query={}, wallet_address="0xabc")
|
|
124
|
+
assert success is False
|
|
125
|
+
assert "missing query" in str(result)
|
|
126
|
+
|
|
127
|
+
@pytest.mark.asyncio
|
|
128
|
+
async def test_get_balance_with_pool_address(
|
|
129
|
+
self, adapter, mock_token_client, mock_wallet_client
|
|
130
|
+
):
|
|
131
|
+
"""Test get_balance with pool address (explicit chain_id)"""
|
|
132
|
+
mock_wallet_client.get_token_balance_for_address = AsyncMock(
|
|
133
|
+
return_value={"balance": 5000000}
|
|
134
|
+
)
|
|
135
|
+
mock_token_client.get_token_details = AsyncMock()
|
|
136
|
+
|
|
137
|
+
success, balance = await adapter.get_balance(
|
|
138
|
+
query="0xPoolAddress",
|
|
139
|
+
wallet_address="0xWallet",
|
|
140
|
+
chain_id=8453,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
assert success is True
|
|
144
|
+
assert balance == 5000000
|
|
145
|
+
mock_wallet_client.get_token_balance_for_address.assert_called_once_with(
|
|
146
|
+
wallet_address="0xWallet",
|
|
147
|
+
query="0xPoolAddress",
|
|
148
|
+
chain_id=8453,
|
|
149
|
+
)
|
|
150
|
+
mock_token_client.get_token_details.assert_not_called()
|
|
151
|
+
|
|
152
|
+
@pytest.mark.asyncio
|
|
153
|
+
async def test_get_balance_token_not_found(self, adapter, mock_token_client):
|
|
154
|
+
"""Test get_balance when token is not found"""
|
|
155
|
+
mock_token_client.get_token_details = AsyncMock(return_value=None)
|
|
156
|
+
|
|
157
|
+
success, error = await adapter.get_balance(
|
|
158
|
+
query="invalid-token",
|
|
159
|
+
wallet_address="0xWallet",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
assert success is False
|
|
163
|
+
assert "Token not found" in str(error)
|
|
164
|
+
|
|
165
|
+
@pytest.mark.asyncio
|
|
166
|
+
async def test_get_balance_missing_chain_id(self, adapter, mock_token_client):
|
|
167
|
+
"""Test get_balance when chain_id cannot be resolved"""
|
|
168
|
+
mock_token_client.get_token_details = AsyncMock(
|
|
169
|
+
return_value={
|
|
170
|
+
"token_id": "token-without-chain",
|
|
171
|
+
"address": "0x123",
|
|
172
|
+
"chain": {},
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
success, error = await adapter.get_balance(
|
|
177
|
+
query="token-without-chain",
|
|
178
|
+
wallet_address="0xWallet",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
assert success is False
|
|
182
|
+
assert "missing a chain id" in str(error)
|
{wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/README.md
RENAMED
|
@@ -46,10 +46,9 @@ success, data = await adapter.get_swap_quote(
|
|
|
46
46
|
slippage=0.01 # 1% slippage
|
|
47
47
|
)
|
|
48
48
|
if success:
|
|
49
|
-
|
|
50
|
-
best_quote = quotes.get("best_quote", {})
|
|
49
|
+
best_quote = data.get("best_quote", {})
|
|
51
50
|
print(f"Output amount: {best_quote.get('output_amount')}")
|
|
52
|
-
print(f"
|
|
51
|
+
print(f"Fee estimate: {best_quote.get('fee_estimate', {}).get('fee_total_usd')}")
|
|
53
52
|
else:
|
|
54
53
|
print(f"Error: {data}")
|
|
55
54
|
```
|
|
@@ -68,8 +67,7 @@ success, data = await adapter.get_best_quote(
|
|
|
68
67
|
)
|
|
69
68
|
if success:
|
|
70
69
|
print(f"Best output: {data.get('output_amount')}")
|
|
71
|
-
print(f"
|
|
72
|
-
print(f"Bridge fee: {data.get('bridge_fee')}")
|
|
70
|
+
print(f"Fee estimate (USD): {data.get('fee_estimate', {}).get('fee_total_usd')}")
|
|
73
71
|
else:
|
|
74
72
|
print(f"Error: {data}")
|
|
75
73
|
```
|
|
@@ -88,10 +86,8 @@ success, data = await adapter.calculate_swap_fees(
|
|
|
88
86
|
if success:
|
|
89
87
|
print(f"Input amount: {data.get('input_amount')}")
|
|
90
88
|
print(f"Output amount: {data.get('output_amount')}")
|
|
91
|
-
print(f"Gas
|
|
92
|
-
print(f"
|
|
93
|
-
print(f"Protocol fee: {data.get('protocol_fee')}")
|
|
94
|
-
print(f"Total fee: {data.get('total_fee')}")
|
|
89
|
+
print(f"Gas estimate: {data.get('gas_fee')}")
|
|
90
|
+
print(f"Total fee (USD): {data.get('total_fee')}")
|
|
95
91
|
print(f"Price impact: {data.get('price_impact')}")
|
|
96
92
|
else:
|
|
97
93
|
print(f"Error: {data}")
|
|
@@ -112,7 +108,7 @@ if success:
|
|
|
112
108
|
print(f"Best route output: {data.get('best_route', {}).get('output_amount')}")
|
|
113
109
|
|
|
114
110
|
for i, route in enumerate(data.get('all_routes', [])):
|
|
115
|
-
print(f"Route {i+1}: Output {route.get('output_amount')}, Fee {route.get('
|
|
111
|
+
print(f\"Route {i+1}: Output {route.get('output_amount')}, Fee USD {route.get('fee_estimate', {}).get('fee_total_usd')}\")
|
|
116
112
|
else:
|
|
117
113
|
print(f"Error: {data}")
|
|
118
114
|
```
|
|
@@ -170,7 +166,7 @@ success, data = await adapter.get_bridge_quote(
|
|
|
170
166
|
slippage=0.01
|
|
171
167
|
)
|
|
172
168
|
if success:
|
|
173
|
-
print(f"Bridge quote received: {data.get('
|
|
169
|
+
print(f"Bridge quote received: {data.get('best_quote', {}).get('output_amount')}")
|
|
174
170
|
else:
|
|
175
171
|
print(f"Error: {data}")
|
|
176
172
|
```
|
|
@@ -196,8 +192,7 @@ if success:
|
|
|
196
192
|
fastest = analysis.get("fastest")
|
|
197
193
|
|
|
198
194
|
print(f"Highest output route: {highest_output.get('output_amount')}")
|
|
199
|
-
print(f"Lowest fees route: {lowest_fees.get('
|
|
200
|
-
print(f"Fastest route: {fastest.get('estimated_time')} seconds")
|
|
195
|
+
print(f\"Lowest fees route (USD): {lowest_fees.get('fee_estimate', {}).get('fee_total_usd')}\")
|
|
201
196
|
```
|
|
202
197
|
|
|
203
198
|
### Fee Analysis
|
|
@@ -215,19 +210,19 @@ success, data = await adapter.calculate_swap_fees(
|
|
|
215
210
|
if success:
|
|
216
211
|
input_amount = int(data.get("input_amount", 0))
|
|
217
212
|
output_amount = int(data.get("output_amount", 0))
|
|
218
|
-
|
|
213
|
+
total_fee_usd = float(data.get("total_fee", 0))
|
|
219
214
|
|
|
220
215
|
# Calculate effective rate
|
|
221
216
|
effective_rate = (input_amount - output_amount) / input_amount
|
|
222
217
|
print(f"Effective rate: {effective_rate:.4f} ({effective_rate * 100:.2f}%)")
|
|
223
|
-
print(f"Total fees: {
|
|
218
|
+
print(f"Total fees: ${total_fee_usd:.4f}")
|
|
224
219
|
```
|
|
225
220
|
|
|
226
221
|
## API Endpoints
|
|
227
222
|
|
|
228
223
|
The adapter uses the following Wayfinder API endpoints:
|
|
229
224
|
|
|
230
|
-
- `
|
|
225
|
+
- `GET /api/v1/blockchain/braps/quote/` - Get swap/bridge quotes
|
|
231
226
|
|
|
232
227
|
## Error Handling
|
|
233
228
|
|