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.

Files changed (148) hide show
  1. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/PKG-INFO +14 -49
  2. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/README.md +13 -48
  3. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/pyproject.toml +1 -1
  4. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  5. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
  6. wayfinder_paths-0.1.14/wayfinder_paths/adapters/balance_adapter/test_adapter.py +182 -0
  7. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/README.md +11 -16
  8. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
  9. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
  10. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
  11. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
  12. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
  13. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/README.md +9 -10
  14. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
  15. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/README.md +2 -14
  16. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
  17. wayfinder_paths-0.1.14/wayfinder_paths/adapters/token_adapter/examples.json +22 -0
  18. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
  19. wayfinder_paths-0.1.14/wayfinder_paths/core/clients/BRAPClient.py +151 -0
  20. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/ClientManager.py +1 -68
  21. wayfinder_paths-0.1.14/wayfinder_paths/core/clients/HyperlendClient.py +253 -0
  22. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/LedgerClient.py +1 -4
  23. wayfinder_paths-0.1.14/wayfinder_paths/core/clients/PoolClient.py +150 -0
  24. wayfinder_paths-0.1.14/wayfinder_paths/core/clients/TokenClient.py +144 -0
  25. wayfinder_paths-0.1.14/wayfinder_paths/core/clients/WalletClient.py +56 -0
  26. wayfinder_paths-0.1.14/wayfinder_paths/core/clients/WayfinderClient.py +131 -0
  27. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/__init__.py +0 -2
  28. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/protocols.py +35 -46
  29. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/clients/sdk_example.py +37 -22
  30. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/engine/StrategyJob.py +7 -55
  31. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/local_evm_txn.py +6 -6
  32. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/local_token_txn.py +1 -1
  33. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/Strategy.py +0 -2
  34. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/utils/evm_helpers.py +2 -2
  35. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/run_strategy.py +8 -19
  36. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
  37. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
  38. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  39. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
  40. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
  41. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
  42. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
  43. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
  44. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/README.md +3 -3
  45. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  46. wayfinder_paths-0.1.13/wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -59
  47. wayfinder_paths-0.1.13/wayfinder_paths/adapters/token_adapter/examples.json +0 -26
  48. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/AuthClient.py +0 -83
  49. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/BRAPClient.py +0 -110
  50. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/HyperlendClient.py +0 -192
  51. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/PoolClient.py +0 -76
  52. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/TokenClient.py +0 -89
  53. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/WalletClient.py +0 -86
  54. wayfinder_paths-0.1.13/wayfinder_paths/core/clients/WayfinderClient.py +0 -263
  55. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/LICENSE +0 -0
  56. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/__init__.py +0 -0
  57. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/abis/generic/erc20.json +0 -0
  58. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/__init__.py +0 -0
  59. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/balance_adapter/examples.json +0 -0
  60. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/brap_adapter/__init__.py +0 -0
  61. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -0
  62. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +0 -0
  63. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +0 -0
  64. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/executor.py +0 -0
  65. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -0
  66. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -0
  67. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +0 -0
  68. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +0 -0
  69. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +0 -0
  70. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -0
  71. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/README.md +0 -0
  72. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -0
  73. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -0
  74. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/examples.json +0 -0
  75. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -0
  76. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/README.md +0 -0
  77. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -0
  78. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/adapter.py +0 -0
  79. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +0 -0
  80. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/__init__.py +0 -0
  81. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/examples.json +0 -0
  82. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -0
  83. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/adapters/token_adapter/__init__.py +0 -0
  84. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/conftest.py +0 -0
  85. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/__init__.py +0 -0
  86. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/BaseAdapter.py +0 -0
  87. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/__init__.py +0 -0
  88. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/base.py +0 -0
  89. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/adapters/models.py +0 -0
  90. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/__init__.py +0 -0
  91. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/bootstrap.py +0 -0
  92. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/stats.py +0 -0
  93. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/analytics/test_analytics.py +0 -0
  94. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/config.py +0 -0
  95. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/__init__.py +0 -0
  96. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/base.py +0 -0
  97. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/erc20_abi.py +0 -0
  98. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/hyperlend_abi.py +0 -0
  99. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/constants/moonwell_abi.py +0 -0
  100. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/engine/__init__.py +0 -0
  101. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/__init__.py +0 -0
  102. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/base.py +0 -0
  103. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/services/web3_service.py +0 -0
  104. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/__init__.py +0 -0
  105. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/base.py +0 -0
  106. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/strategies/descriptors.py +0 -0
  107. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/utils/__init__.py +0 -0
  108. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/utils/wallets.py +0 -0
  109. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/wallets/README.md +0 -0
  110. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/wallets/WalletManager.py +0 -0
  111. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/core/wallets/__init__.py +0 -0
  112. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/enso.py +0 -0
  113. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/erc20.py +0 -0
  114. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/evm.py +0 -0
  115. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/hyper_evm.py +0 -0
  116. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/hyperlend.py +0 -0
  117. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/hyperliquid.py +0 -0
  118. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/moonwell.py +0 -0
  119. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/prjx.py +0 -0
  120. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/policies/util.py +0 -0
  121. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/__init__.py +0 -0
  122. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/create_strategy.py +0 -0
  123. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/make_wallets.py +0 -0
  124. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/scripts/run_strategy.py +0 -0
  125. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/__init__.py +0 -0
  126. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/README.md +0 -0
  127. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/__init__.py +0 -0
  128. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/constants.py +0 -0
  129. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/examples.json +0 -0
  130. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -0
  131. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +0 -0
  132. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -0
  133. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/config.py +0 -0
  134. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -0
  135. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +0 -0
  136. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +0 -0
  137. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +0 -0
  138. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/strategies/stablecoin_yield_strategy/examples.json +0 -0
  139. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/README.md +0 -0
  140. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/adapter.py +0 -0
  141. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/examples.json +0 -0
  142. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/adapter/test_adapter.py +0 -0
  143. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/examples.json +0 -0
  144. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/templates/strategy/strategy.py +0 -0
  145. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/__init__.py +0 -0
  146. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/test_smoke_manifest.py +0 -0
  147. {wayfinder_paths-0.1.13 → wayfinder_paths-0.1.14}/wayfinder_paths/tests/test_test_coverage.py +0 -0
  148. {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.13
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 supports two authentication methods:
219
+ Wayfinder Paths uses API key authentication via the `X-API-KEY` header.
220
220
 
221
- #### 1. Service Account Authentication (API Key)
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..." // Alternative: system-level API key
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 keys are automatically discovered by all clients (no need to pass explicitly)
270
- - When an API key is available, it's used for all API requests (including public endpoints) for rate limiting
271
- - If no API key is found, the system falls back to OAuth authentication
272
- - All clients created by adapters automatically inherit the API key discovery mechanism
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 authentication documentation.
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=",".join(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
- token_id=self.config.get("token_id"),
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
- token_id=self.config.get("token_id"),
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 supports two authentication methods:
198
+ Wayfinder Paths uses API key authentication via the `X-API-KEY` header.
199
199
 
200
- #### 1. Service Account Authentication (API Key)
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..." // Alternative: system-level API key
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 keys are automatically discovered by all clients (no need to pass explicitly)
249
- - When an API key is available, it's used for all API requests (including public endpoints) for rate limiting
250
- - If no API key is found, the system falls back to OAuth authentication
251
- - All clients created by adapters automatically inherit the API key discovery mechanism
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 authentication documentation.
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=",".join(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
- token_id=self.config.get("token_id"),
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
- token_id=self.config.get("token_id"),
304
+ query=self.config.get("token_id"),
340
305
  wallet_address=self.config.get("strategy_wallet", {}).get("address"),
341
306
  )
342
307
  return {
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "wayfinder-paths"
3
- version = "0.1.13"
3
+ version = "0.1.14"
4
4
  description = "Wayfinder Path: strategies and adapters"
5
5
  readme = "README.md"
6
6
  authors = ["Wayfinder <dev@wayfinder.ai>"]
@@ -23,24 +23,23 @@ balance = BalanceAdapter(config, web3_service=web3_service)
23
23
 
24
24
  ## API surface
25
25
 
26
- ### `get_balance(token_id: str, wallet_address: str)`
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
- token_id="usd-coin-base",
34
+ query="usd-coin-base",
32
35
  wallet_address=config["main_wallet"]["address"],
33
36
  )
34
- ```
35
37
 
36
- ### `get_pool_balance(pool_address: str, chain_id: int, user_address: str)`
37
- Fetches the amount supplied to a specific pool, using the `/wallets/pool-balance` endpoint.
38
-
39
- ```python
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.get_pool_balance(
84
- pool_address=self.current_pool["address"],
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
- token_id: str,
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
- data = await self.wallet_client.get_token_balance_for_wallet(
51
- token_id=token_id,
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
- return (True, data.get("balance"))
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": token_info.get("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=token_info.get("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=token_info.get("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)
@@ -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
- quotes = data.get("quotes", {})
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"Total fee: {best_quote.get('total_fee')}")
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"Gas fee: {data.get('gas_fee')}")
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 fee: {data.get('gas_fee')}")
92
- print(f"Bridge fee: {data.get('bridge_fee')}")
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('total_fee')}")
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('quotes', {}).get('best_quote', {}).get('output_amount')}")
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('total_fee')}")
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
- total_fee = int(data.get("total_fee", 0))
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: {total_fee / 1e18:.6f} tokens")
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
- - `POST /api/v1/public/quotes/` - Get swap/bridge quotes
225
+ - `GET /api/v1/blockchain/braps/quote/` - Get swap/bridge quotes
231
226
 
232
227
  ## Error Handling
233
228