wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wayfinder-paths might be problematic. Click here for more details.

Files changed (47) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
  4. wayfinder_paths/adapters/brap_adapter/README.md +11 -16
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
  6. wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
  7. wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
  9. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
  10. wayfinder_paths/adapters/pool_adapter/README.md +9 -10
  11. wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
  12. wayfinder_paths/adapters/token_adapter/README.md +2 -14
  13. wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
  14. wayfinder_paths/adapters/token_adapter/examples.json +4 -8
  15. wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
  16. wayfinder_paths/core/clients/BRAPClient.py +102 -61
  17. wayfinder_paths/core/clients/ClientManager.py +1 -68
  18. wayfinder_paths/core/clients/HyperlendClient.py +125 -64
  19. wayfinder_paths/core/clients/LedgerClient.py +1 -4
  20. wayfinder_paths/core/clients/PoolClient.py +122 -48
  21. wayfinder_paths/core/clients/TokenClient.py +91 -36
  22. wayfinder_paths/core/clients/WalletClient.py +26 -56
  23. wayfinder_paths/core/clients/WayfinderClient.py +28 -160
  24. wayfinder_paths/core/clients/__init__.py +0 -2
  25. wayfinder_paths/core/clients/protocols.py +35 -46
  26. wayfinder_paths/core/clients/sdk_example.py +37 -22
  27. wayfinder_paths/core/engine/StrategyJob.py +7 -55
  28. wayfinder_paths/core/services/local_evm_txn.py +6 -6
  29. wayfinder_paths/core/services/local_token_txn.py +1 -1
  30. wayfinder_paths/core/strategies/Strategy.py +0 -2
  31. wayfinder_paths/core/utils/evm_helpers.py +2 -2
  32. wayfinder_paths/run_strategy.py +8 -19
  33. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
  34. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
  35. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  36. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
  37. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
  38. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
  39. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
  40. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
  41. wayfinder_paths/templates/strategy/README.md +3 -3
  42. wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  43. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +14 -49
  44. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +46 -47
  45. wayfinder_paths/core/clients/AuthClient.py +0 -83
  46. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
  47. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/WHEEL +0 -0
@@ -34,31 +34,31 @@ class TestHyperlendAdapter:
34
34
  async def test_get_stable_markets_success(self, adapter, mock_hyperlend_client):
35
35
  """Test successful stable markets retrieval"""
36
36
  mock_response = {
37
- "markets": [
38
- {
39
- "chain_id": 999,
40
- "underlying_token": "0x1234...",
37
+ "markets": {
38
+ "0x1234...": {
41
39
  "symbol": "USDT",
42
- "apy": 0.05,
43
- "available_liquidity": 1000000,
44
- "buffer_bps": 100,
45
- "min_buffer_tokens": 100.0,
40
+ "symbol_canonical": "usdt",
41
+ "display_symbol": "USDT",
42
+ "reserve": {},
43
+ "decimals": 6,
44
+ "headroom": 1000000000000,
45
+ "supply_cap": 5000000000000,
46
46
  },
47
- {
48
- "chain_id": 999,
49
- "underlying_token": "0x5678...",
47
+ "0x5678...": {
50
48
  "symbol": "USDC",
51
- "apy": 0.04,
52
- "available_liquidity": 2000000,
53
- "buffer_bps": 100,
54
- "min_buffer_tokens": 100.0,
49
+ "symbol_canonical": "usdc",
50
+ "display_symbol": "USDC",
51
+ "reserve": {},
52
+ "decimals": 6,
53
+ "headroom": 2000000000000,
54
+ "supply_cap": 10000000000000,
55
55
  },
56
- ]
56
+ },
57
+ "notes": [],
57
58
  }
58
59
  mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
59
60
 
60
61
  success, data = await adapter.get_stable_markets(
61
- chain_id=999,
62
62
  required_underlying_tokens=1000.0,
63
63
  buffer_bps=100,
64
64
  min_buffer_tokens=100.0,
@@ -67,11 +67,9 @@ class TestHyperlendAdapter:
67
67
  assert success is True
68
68
  assert data == mock_response
69
69
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
70
- chain_id=999,
71
70
  required_underlying_tokens=1000.0,
72
71
  buffer_bps=100,
73
72
  min_buffer_tokens=100.0,
74
- is_stable_symbol=None,
75
73
  )
76
74
 
77
75
  @pytest.mark.asyncio
@@ -80,27 +78,29 @@ class TestHyperlendAdapter:
80
78
  ):
81
79
  """Test stable markets retrieval with only required chain_id"""
82
80
  mock_response = {
83
- "markets": [
84
- {
85
- "chain_id": 999,
86
- "underlying_token": "0x1234...",
81
+ "markets": {
82
+ "0x1234...": {
87
83
  "symbol": "USDT",
88
- "apy": 0.05,
84
+ "symbol_canonical": "usdt",
85
+ "display_symbol": "USDT",
86
+ "reserve": {},
87
+ "decimals": 6,
88
+ "headroom": 1000000000000,
89
+ "supply_cap": 5000000000000,
89
90
  }
90
- ]
91
+ },
92
+ "notes": [],
91
93
  }
92
94
  mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
93
95
 
94
- success, data = await adapter.get_stable_markets(chain_id=999)
96
+ success, data = await adapter.get_stable_markets()
95
97
 
96
98
  assert success is True
97
99
  assert data == mock_response
98
100
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
99
- chain_id=999,
100
101
  required_underlying_tokens=None,
101
102
  buffer_bps=None,
102
103
  min_buffer_tokens=None,
103
- is_stable_symbol=None,
104
104
  )
105
105
 
106
106
  @pytest.mark.asyncio
@@ -108,21 +108,19 @@ class TestHyperlendAdapter:
108
108
  self, adapter, mock_hyperlend_client
109
109
  ):
110
110
  """Test stable markets retrieval with partial optional parameters"""
111
- mock_response = {"markets": []}
111
+ mock_response = {"markets": {}, "notes": []}
112
112
  mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
113
113
 
114
114
  success, data = await adapter.get_stable_markets(
115
- chain_id=999, required_underlying_tokens=500.0
115
+ required_underlying_tokens=500.0
116
116
  )
117
117
 
118
118
  assert success is True
119
119
  assert data == mock_response
120
120
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
121
- chain_id=999,
122
121
  required_underlying_tokens=500.0,
123
122
  buffer_bps=None,
124
123
  min_buffer_tokens=None,
125
- is_stable_symbol=None,
126
124
  )
127
125
 
128
126
  @pytest.mark.asyncio
@@ -132,7 +130,7 @@ class TestHyperlendAdapter:
132
130
  side_effect=Exception("API Error: Connection timeout")
133
131
  )
134
132
 
135
- success, data = await adapter.get_stable_markets(chain_id=999)
133
+ success, data = await adapter.get_stable_markets()
136
134
 
137
135
  assert success is False
138
136
  assert "API Error: Connection timeout" in data
@@ -144,7 +142,7 @@ class TestHyperlendAdapter:
144
142
  side_effect=Exception("HTTP 404 Not Found")
145
143
  )
146
144
 
147
- success, data = await adapter.get_stable_markets(chain_id=999)
145
+ success, data = await adapter.get_stable_markets()
148
146
 
149
147
  assert success is False
150
148
  assert "404" in data or "Not Found" in data
@@ -154,14 +152,14 @@ class TestHyperlendAdapter:
154
152
  self, adapter, mock_hyperlend_client
155
153
  ):
156
154
  """Test stable markets retrieval with empty response"""
157
- mock_response = {"markets": []}
155
+ mock_response = {"markets": {}, "notes": []}
158
156
  mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
159
157
 
160
- success, data = await adapter.get_stable_markets(chain_id=999)
158
+ success, data = await adapter.get_stable_markets()
161
159
 
162
160
  assert success is True
163
161
  assert data == mock_response
164
- assert len(data.get("markets", [])) == 0
162
+ assert len(data.get("markets", {})) == 0
165
163
 
166
164
  def test_adapter_type(self, adapter):
167
165
  """Test adapter has adapter_type"""
@@ -186,59 +184,95 @@ class TestHyperlendAdapter:
186
184
  async def test_get_stable_markets_with_is_stable_symbol(
187
185
  self, adapter, mock_hyperlend_client
188
186
  ):
189
- """Test stable markets retrieval with is_stable_symbol parameter"""
187
+ """Test stable markets retrieval with is_stable_symbol parameter (ignored by API)"""
190
188
  mock_response = {
191
- "markets": [
192
- {
193
- "chain_id": 999,
194
- "underlying_token": "0x1234...",
189
+ "markets": {
190
+ "0x1234...": {
195
191
  "symbol": "USDT",
196
- "apy": 0.05,
192
+ "symbol_canonical": "usdt",
193
+ "display_symbol": "USDT",
194
+ "reserve": {},
195
+ "decimals": 6,
196
+ "headroom": 1000000000000,
197
+ "supply_cap": 5000000000000,
197
198
  }
198
- ]
199
+ },
200
+ "notes": [],
199
201
  }
200
202
  mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
201
203
 
202
- success, data = await adapter.get_stable_markets(
203
- chain_id=999, is_stable_symbol=True
204
- )
204
+ success, data = await adapter.get_stable_markets()
205
205
 
206
206
  assert success is True
207
207
  assert data == mock_response
208
208
  mock_hyperlend_client.get_stable_markets.assert_called_once_with(
209
- chain_id=999,
210
209
  required_underlying_tokens=None,
211
210
  buffer_bps=None,
212
211
  min_buffer_tokens=None,
213
- is_stable_symbol=True,
214
212
  )
215
213
 
216
214
  @pytest.mark.asyncio
217
215
  async def test_get_assets_view_success(self, adapter, mock_hyperlend_client):
218
216
  """Test successful assets view retrieval"""
219
217
  mock_response = {
218
+ "block_number": 12345,
219
+ "user": "0x0c737cB5934afCb5B01965141F865F795B324080",
220
+ "native_balance_wei": 0,
221
+ "native_balance": 0.0,
220
222
  "assets": [
221
223
  {
222
- "token_address": "0x1234...",
224
+ "underlying": "0x1234...",
223
225
  "symbol": "USDT",
224
- "balance": "1000.0",
225
- "supplied": "500.0",
226
- "borrowed": "0.0",
226
+ "symbol_canonical": "usdt",
227
+ "symbol_display": "USDT",
228
+ "decimals": 6,
229
+ "a_token": "0x...",
230
+ "variable_debt_token": "0x...",
231
+ "usage_as_collateral_enabled": True,
232
+ "borrowing_enabled": True,
233
+ "is_active": True,
234
+ "is_frozen": False,
235
+ "is_paused": False,
236
+ "is_siloed_borrowing": False,
237
+ "is_stablecoin": True,
238
+ "underlying_wallet_balance": 1000.0,
239
+ "underlying_wallet_balance_wei": 1000000000,
240
+ "price_usd": 1.0,
241
+ "supply": 500.0,
242
+ "variable_borrow": 0.0,
243
+ "supply_usd": 500.0,
244
+ "variable_borrow_usd": 0.0,
245
+ "supply_apr": 0.05,
246
+ "supply_apy": 0.05,
247
+ "variable_borrow_apr": 0.07,
248
+ "variable_borrow_apy": 0.07,
227
249
  }
228
250
  ],
229
- "total_value": 1000.0,
251
+ "account_data": {
252
+ "total_collateral_base": 500,
253
+ "total_debt_base": 0,
254
+ "available_borrows_base": 400,
255
+ "current_liquidation_threshold": 8000,
256
+ "ltv": 7500,
257
+ "health_factor_wad": 115792089237316195423570985008687907853269984665640564039457584007913129639935,
258
+ "health_factor": 1.157920892373162e59,
259
+ },
260
+ "base_currency_info": {
261
+ "marketReferenceCurrencyUnit": 100000000,
262
+ "marketReferenceCurrencyPriceInUsd": 100000000,
263
+ "networkBaseTokenPriceInUsd": 0,
264
+ "networkBaseTokenPriceDecimals": 8,
265
+ },
230
266
  }
231
267
  mock_hyperlend_client.get_assets_view = AsyncMock(return_value=mock_response)
232
268
 
233
269
  success, data = await adapter.get_assets_view(
234
- chain_id=999,
235
270
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
236
271
  )
237
272
 
238
273
  assert success is True
239
274
  assert data == mock_response
240
275
  mock_hyperlend_client.get_assets_view.assert_called_once_with(
241
- chain_id=999,
242
276
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
243
277
  )
244
278
 
@@ -250,7 +284,6 @@ class TestHyperlendAdapter:
250
284
  )
251
285
 
252
286
  success, data = await adapter.get_assets_view(
253
- chain_id=999,
254
287
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
255
288
  )
256
289
 
@@ -260,15 +293,36 @@ class TestHyperlendAdapter:
260
293
  @pytest.mark.asyncio
261
294
  async def test_get_assets_view_empty_response(self, adapter, mock_hyperlend_client):
262
295
  """Test assets view retrieval with empty response"""
263
- mock_response = {"assets": [], "total_value": 0.0}
296
+ mock_response = {
297
+ "block_number": 12345,
298
+ "user": "0x0c737cB5934afCb5B01965141F865F795B324080",
299
+ "native_balance_wei": 0,
300
+ "native_balance": 0.0,
301
+ "assets": [],
302
+ "account_data": {
303
+ "total_collateral_base": 0,
304
+ "total_debt_base": 0,
305
+ "available_borrows_base": 0,
306
+ "current_liquidation_threshold": 0,
307
+ "ltv": 0,
308
+ "health_factor_wad": 0,
309
+ "health_factor": 0.0,
310
+ },
311
+ "base_currency_info": {
312
+ "marketReferenceCurrencyUnit": 100000000,
313
+ "marketReferenceCurrencyPriceInUsd": 100000000,
314
+ "networkBaseTokenPriceInUsd": 0,
315
+ "networkBaseTokenPriceDecimals": 8,
316
+ },
317
+ }
264
318
  mock_hyperlend_client.get_assets_view = AsyncMock(return_value=mock_response)
265
319
 
266
320
  success, data = await adapter.get_assets_view(
267
- chain_id=999,
268
321
  user_address="0x0c737cB5934afCb5B01965141F865F795B324080",
269
322
  )
270
323
 
271
324
  assert success is True
272
325
  assert data == mock_response
273
326
  assert len(data.get("assets", [])) == 0
274
- assert data.get("total_value") == 0.0
327
+ # New API uses account_data; total_value may not be present
328
+ assert data.get("account_data", {}).get("total_collateral_base") == 0
@@ -44,30 +44,29 @@ else:
44
44
  print(f"Error: {data}")
45
45
  ```
46
46
 
47
- ### Get Llama Matches
47
+ ### Get pools (all or filtered)
48
+
49
+ Pass at least `chain_id` when fetching all pools (e.g. `chain_id=8453` for Base):
48
50
 
49
51
  ```python
50
- success, data = await adapter.get_pools()
52
+ success, data = await adapter.get_pools(chain_id=8453)
51
53
  if success:
52
54
  matches = data.get("matches", [])
53
- print(f"Found {len(matches)} Llama matches")
54
55
  for match in matches:
55
56
  if match.get("stablecoin"):
56
- print(f"Stablecoin pool: {match.get('id')} - APY: {match.get('apy')}%")
57
+ print(f"Pool {match.get('id')} - APY: {match.get('apy')}%")
57
58
  else:
58
59
  print(f"Error: {data}")
59
60
  ```
60
61
 
61
- ## Advanced Usage
62
+ Optional: `project="lido"` to filter by project.
62
63
 
63
64
  ## API Endpoints
64
65
 
65
- The adapter uses the following Wayfinder API endpoints:
66
+ The adapter uses the Wayfinder API:
66
67
 
67
- - `GET /api/v1/public/pools/?pool_ids=X` - Get pools by IDs
68
- - `GET /api/v1/public/pools/` - Get all pools
69
- - `GET /api/v1/public/pools/llama/matches/` - Get Llama matches
70
- - `GET /api/v1/public/pools/llama/reports/` - Get Llama reports
68
+ - `GET /v1/blockchain/pools/?chain_id=1&project=lido` - List pools (filter by chain_id, optional project)
69
+ - `POST /v1/blockchain/pools/` - Get pools by IDs (body: `{"pool_ids": ["id1", "id2"]}`)
71
70
 
72
71
  ## Error Handling
73
72
 
@@ -2,7 +2,7 @@ from typing import Any
2
2
 
3
3
  from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
4
4
  from wayfinder_paths.core.clients.PoolClient import (
5
- LlamaMatch,
5
+ LlamaMatchesResponse,
6
6
  PoolClient,
7
7
  PoolList,
8
8
  )
@@ -48,16 +48,15 @@ class PoolAdapter(BaseAdapter):
48
48
  self.logger.error(f"Error fetching pools by IDs: {e}")
49
49
  return (False, str(e))
50
50
 
51
- async def get_pools(self) -> tuple[bool, dict[str, LlamaMatch] | str]:
52
- """
53
- Get Llama protocol matches for pools.
54
-
55
- Returns:
56
- Tuple of (success, data) where data is Llama matches or error message
57
- """
51
+ async def get_pools(
52
+ self,
53
+ *,
54
+ chain_id: int | None = None,
55
+ project: str | None = None,
56
+ ) -> tuple[bool, LlamaMatchesResponse | str]:
58
57
  try:
59
- data = await self.pool_client.get_pools()
58
+ data = await self.pool_client.get_pools(chain_id=chain_id, project=project)
60
59
  return (True, data)
61
60
  except Exception as e:
62
- self.logger.error(f"Error fetching Llama matches: {e}")
61
+ self.logger.error(f"Error fetching pools: {e}")
63
62
  return (False, str(e))
@@ -63,23 +63,11 @@ else:
63
63
  print(f"Error: {data}")
64
64
  ```
65
65
 
66
- ### Get Token (Flexible)
67
-
68
- ```python
69
- # Try by address first, then by token_id
70
- success, data = await adapter.get_token(address="0x1234...", token_id="token-123")
71
- if success:
72
- print(f"Token data: {data}")
73
- else:
74
- print(f"Error: {data}")
75
- ```
76
-
77
66
  ## API Endpoints
78
67
 
79
- The adapter uses the following existing public API endpoints:
68
+ The adapter uses the following Wayfinder API endpoint:
80
69
 
81
- 1. **Token Details** (by address): `GET /public/tokens/detail/?query={address}&get_chart=false`
82
- 2. **Token by ID**: `GET /public/tokens/lookup/?token_id={token_id}`
70
+ - `GET /api/v1/blockchain/tokens/detail/?query=...&market_data=...&chain_id=...`
83
71
 
84
72
  ## Error Handling
85
73
 
@@ -24,7 +24,9 @@ class TokenAdapter(BaseAdapter):
24
24
  super().__init__("token_adapter", config)
25
25
  self.token_client = token_client or TokenClient()
26
26
 
27
- async def get_token(self, query: str) -> tuple[bool, TokenDetails | str]:
27
+ async def get_token(
28
+ self, query: str, *, chain_id: int | None = None
29
+ ) -> tuple[bool, TokenDetails | str]:
28
30
  """
29
31
  Get token data by address using the token-details endpoint.
30
32
 
@@ -35,7 +37,7 @@ class TokenAdapter(BaseAdapter):
35
37
  Tuple of (success, data) where data is the token information or error message
36
38
  """
37
39
  try:
38
- data = await self.token_client.get_token_details(query)
40
+ data = await self.token_client.get_token_details(query, chain_id=chain_id)
39
41
  if not data:
40
42
  return (False, f"No token found for: {query}")
41
43
  return (True, data)
@@ -43,7 +45,9 @@ class TokenAdapter(BaseAdapter):
43
45
  self.logger.error(f"Error getting token by query {query}: {e}")
44
46
  return (False, str(e))
45
47
 
46
- async def get_token_price(self, token_id: str) -> tuple[bool, dict[str, Any] | str]:
48
+ async def get_token_price(
49
+ self, token_id: str, *, chain_id: int | None = None
50
+ ) -> tuple[bool, dict[str, Any] | str]:
47
51
  """
48
52
  Get token price by token ID or address using the token-details endpoint.
49
53
 
@@ -54,19 +58,21 @@ class TokenAdapter(BaseAdapter):
54
58
  Tuple of (success, data) where data is the price information or error message
55
59
  """
56
60
  try:
57
- data = await self.token_client.get_token_details(token_id)
61
+ data = await self.token_client.get_token_details(
62
+ token_id, market_data=True, chain_id=chain_id
63
+ )
58
64
  if not data:
59
65
  return (False, f"No token found for: {token_id}")
60
66
 
61
- # Extract just the price information
67
+ price_change_24h = data.get("price_change_24h", 0.0)
62
68
  price_data = {
63
69
  "current_price": data.get("current_price", 0.0),
64
- "price_change_24h": data.get("price_change_24h", 0.0),
65
- "price_change_percentage_24h": data.get(
66
- "price_change_percentage_24h", 0.0
67
- ),
70
+ "price_change_24h": price_change_24h,
71
+ "price_change_percentage_24h": data.get("price_change_percentage_24h")
72
+ if data.get("price_change_percentage_24h") is not None
73
+ else (float(price_change_24h) * 100.0 if price_change_24h else 0.0),
68
74
  "market_cap": data.get("market_cap", 0),
69
- "total_volume": data.get("total_volume", 0),
75
+ "total_volume": data.get("total_volume_usd_24h", 0),
70
76
  "symbol": data.get("symbol", ""),
71
77
  "name": data.get("name", ""),
72
78
  "address": data.get("address", ""),
@@ -1,19 +1,15 @@
1
1
  {
2
2
  "basic_usage": {
3
3
  "description": "Basic usage of TokenAdapter to get token information",
4
- "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Get token by address\nsuccess, data = await adapter.get_token_by_address(\"0x1234567890abcdef1234567890abcdef12345678\")\nif success:\n print(f\"Token symbol: {data.get('symbol')}\")\n print(f\"Token name: {data.get('name')}\")\n print(f\"Decimals: {data.get('decimals')}\")\nelse:\n print(f\"Error: {data}\")"
4
+ "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Get token by address (provide chain_id when querying by address)\nsuccess, data = await adapter.get_token(\n \"0x1234567890abcdef1234567890abcdef12345678\",\n chain_id=8453,\n)\nif success:\n print(f\"Token symbol: {data.get('symbol')}\")\n print(f\"Token name: {data.get('name')}\")\n print(f\"Decimals: {data.get('decimals')}\")\nelse:\n print(f\"Error: {data}\")"
5
5
  },
6
6
  "get_by_token_id": {
7
7
  "description": "Get token information using token ID",
8
- "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Get token by ID\nsuccess, data = await adapter.get_token_by_id(\"token-12345\")\nif success:\n print(f\"Token data: {data}\")\nelse:\n print(f\"Error: {data}\")"
9
- },
10
- "flexible_lookup": {
11
- "description": "Use flexible get_token method that tries both address and token_id",
12
- "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Try both address and token_id\nsuccess, data = await adapter.get_token(\n address=\"0x1234567890abcdef1234567890abcdef12345678\",\n token_id=\"token-12345\"\n)\nif success:\n print(f\"Found token: {data}\")\nelse:\n print(f\"Token not found: {data}\")"
8
+ "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Get token by ID\nsuccess, data = await adapter.get_token(\"token-12345\")\nif success:\n print(f\"Token data: {data}\")\nelse:\n print(f\"Error: {data}\")"
13
9
  },
14
10
  "error_handling": {
15
11
  "description": "Proper error handling for various scenarios",
16
- "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Handle missing parameters\ntry:\n success, data = await adapter.get_token()\n if not success:\n print(f\"Error: {data}\")\nexcept Exception as e:\n print(f\"Unexpected error: {e}\")\n\n# Handle API errors\nsuccess, data = await adapter.get_token_by_address(\"invalid-address\")\nif not success:\n print(f\"API error: {data}\")\nelse:\n print(f\"Token found: {data}\")"
12
+ "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# Handle API errors\nsuccess, data = await adapter.get_token(\"invalid-address\")\nif not success:\n print(f\"API error: {data}\")\nelse:\n print(f\"Token found: {data}\")"
17
13
  },
18
14
  "health_check": {
19
15
  "description": "Check adapter health and connectivity",
@@ -21,6 +17,6 @@
21
17
  },
22
18
  "batch_operations": {
23
19
  "description": "Perform multiple token lookups efficiently",
24
- "code": "from adapters.token_adapter.adapter import TokenAdapter\nimport asyncio\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# List of token addresses to lookup\ntoken_addresses = [\n \"0x1234567890abcdef1234567890abcdef12345678\",\n \"0xabcdef1234567890abcdef1234567890abcdef12\",\n \"0x9876543210fedcba9876543210fedcba98765432\"\n]\n\n# Batch lookup\ntoken_data = {}\nfor address in token_addresses:\n success, data = await adapter.get_token_by_address(address)\n if success:\n token_data[address] = data\n else:\n print(f\"Failed to get token for {address}: {data}\")\n\nprint(f\"Successfully retrieved {len(token_data)} tokens\")\nfor address, data in token_data.items():\n print(f\"{address}: {data.get('symbol', 'Unknown')} - {data.get('name', 'Unknown')}\")"
20
+ "code": "from adapters.token_adapter.adapter import TokenAdapter\n\n# Initialize adapter (no config needed)\nadapter = TokenAdapter()\n\n# List of token addresses to lookup (Base chain)\ntoken_addresses = [\n \"0x1234567890abcdef1234567890abcdef12345678\",\n \"0xabcdef1234567890abcdef1234567890abcdef12\",\n \"0x9876543210fedcba9876543210fedcba98765432\"\n]\n\n# Batch lookup\ntoken_data = {}\nfor address in token_addresses:\n success, data = await adapter.get_token(address, chain_id=8453)\n if success:\n token_data[address] = data\n else:\n print(f\"Failed to get token for {address}: {data}\")\n\nprint(f\"Successfully retrieved {len(token_data)} tokens\")\nfor address, data in token_data.items():\n print(f\"{address}: {data.get('symbol', 'Unknown')} - {data.get('name', 'Unknown')}\")"
25
21
  }
26
22
  }
@@ -68,9 +68,8 @@ class TestTokenAdapter:
68
68
  mock_token_data = {
69
69
  "current_price": 1.50,
70
70
  "price_change_24h": 0.05,
71
- "price_change_percentage_24h": 3.45,
72
71
  "market_cap": 1000000,
73
- "total_volume": 50000,
72
+ "total_volume_usd_24h": 50000,
74
73
  "symbol": "TEST",
75
74
  "name": "Test Token",
76
75
  "address": "0x1234...",
@@ -85,6 +84,8 @@ class TestTokenAdapter:
85
84
  assert data["current_price"] == 1.50
86
85
  assert data["symbol"] == "TEST"
87
86
  assert data["name"] == "Test Token"
87
+ assert data["total_volume"] == 50000
88
+ assert data["price_change_percentage_24h"] == 5.0 # 0.05 * 100
88
89
 
89
90
  @pytest.mark.asyncio
90
91
  async def test_get_token_price_not_found(self, adapter):
@@ -99,7 +100,8 @@ class TestTokenAdapter:
99
100
  async def test_get_gas_token_success(self, adapter):
100
101
  """Test successful gas token retrieval"""
101
102
  mock_gas_token_data = {
102
- "id": "ethereum-base",
103
+ "id": "ethereum_0x0000000000000000000000000000000000000000",
104
+ "token_id": "ethereum_0x0000000000000000000000000000000000000000",
103
105
  "symbol": "ETH",
104
106
  "name": "Ethereum",
105
107
  "address": "0x0000000000000000000000000000000000000000",