wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.15__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 (61) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +73 -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 +144 -78
  6. wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
  7. wayfinder_paths/adapters/brap_adapter/test_adapter.py +127 -65
  8. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +30 -14
  9. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +121 -67
  10. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +6 -6
  11. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +12 -12
  12. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +6 -6
  13. wayfinder_paths/adapters/moonwell_adapter/adapter.py +332 -9
  14. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +13 -13
  15. wayfinder_paths/adapters/pool_adapter/README.md +9 -10
  16. wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
  17. wayfinder_paths/adapters/pool_adapter/test_adapter.py +2 -2
  18. wayfinder_paths/adapters/token_adapter/README.md +2 -14
  19. wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
  20. wayfinder_paths/adapters/token_adapter/examples.json +4 -8
  21. wayfinder_paths/adapters/token_adapter/test_adapter.py +9 -7
  22. wayfinder_paths/core/clients/BRAPClient.py +102 -61
  23. wayfinder_paths/core/clients/ClientManager.py +1 -68
  24. wayfinder_paths/core/clients/HyperlendClient.py +125 -64
  25. wayfinder_paths/core/clients/LedgerClient.py +1 -4
  26. wayfinder_paths/core/clients/PoolClient.py +122 -48
  27. wayfinder_paths/core/clients/TokenClient.py +91 -36
  28. wayfinder_paths/core/clients/WalletClient.py +26 -56
  29. wayfinder_paths/core/clients/WayfinderClient.py +28 -160
  30. wayfinder_paths/core/clients/__init__.py +0 -2
  31. wayfinder_paths/core/clients/protocols.py +35 -46
  32. wayfinder_paths/core/clients/sdk_example.py +37 -22
  33. wayfinder_paths/core/constants/erc20_abi.py +0 -11
  34. wayfinder_paths/core/engine/StrategyJob.py +10 -56
  35. wayfinder_paths/core/services/base.py +1 -0
  36. wayfinder_paths/core/services/local_evm_txn.py +25 -9
  37. wayfinder_paths/core/services/local_token_txn.py +2 -6
  38. wayfinder_paths/core/services/test_local_evm_txn.py +145 -0
  39. wayfinder_paths/core/strategies/Strategy.py +16 -4
  40. wayfinder_paths/core/utils/evm_helpers.py +2 -9
  41. wayfinder_paths/policies/erc20.py +1 -1
  42. wayfinder_paths/run_strategy.py +13 -19
  43. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +77 -11
  44. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +6 -6
  45. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +107 -23
  46. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  47. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +6 -5
  48. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2246 -1279
  49. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +276 -109
  50. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
  51. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +153 -56
  52. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
  53. wayfinder_paths/templates/adapter/README.md +1 -1
  54. wayfinder_paths/templates/strategy/README.md +3 -3
  55. wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  56. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/METADATA +14 -49
  57. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/RECORD +59 -60
  58. wayfinder_paths/abis/generic/erc20.json +0 -383
  59. wayfinder_paths/core/clients/AuthClient.py +0 -83
  60. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/LICENSE +0 -0
  61. {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/WHEEL +0 -0
@@ -41,14 +41,34 @@ class TestBRAPAdapter:
41
41
  async def test_get_swap_quote_success(self, adapter, mock_brap_client):
42
42
  """Test successful swap quote retrieval"""
43
43
  mock_response = {
44
- "quotes": {
45
- "best_quote": {
46
- "input_amount": "1000000000000000000",
47
- "output_amount": "995000000000000000",
48
- "gas_fee": "5000000000000000",
49
- "total_fee": "8000000000000000",
44
+ "quotes": [
45
+ {
46
+ "provider": "enso",
47
+ "input_amount": 1000000000000000000,
48
+ "output_amount": 995000000000000000,
49
+ "calldata": {
50
+ "data": "0x",
51
+ "to": "0x",
52
+ "from_address": "0x",
53
+ "value": "0",
54
+ "chainId": 8453,
55
+ },
56
+ "fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
50
57
  }
51
- }
58
+ ],
59
+ "best_quote": {
60
+ "provider": "enso",
61
+ "input_amount": 1000000000000000000,
62
+ "output_amount": 995000000000000000,
63
+ "calldata": {
64
+ "data": "0x",
65
+ "to": "0x",
66
+ "from_address": "0x",
67
+ "value": "0",
68
+ "chainId": 8453,
69
+ },
70
+ "fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
71
+ },
52
72
  }
53
73
  mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
54
74
 
@@ -63,29 +83,35 @@ class TestBRAPAdapter:
63
83
  slippage=0.01,
64
84
  )
65
85
 
66
- assert success is True
86
+ assert success
67
87
  assert data == mock_response
68
88
  mock_brap_client.get_quote.assert_called_once_with(
69
- from_token_address="0x" + "a" * 40,
70
- to_token_address="0x" + "b" * 40,
71
- from_chain_id=8453,
72
- to_chain_id=1,
73
- from_address="0x1234567890123456789012345678901234567890",
74
- to_address="0x1234567890123456789012345678901234567890",
75
- amount1="1000000000000000000",
76
- slippage=0.01,
77
- wayfinder_fee=None,
89
+ from_token="0x" + "a" * 40,
90
+ to_token="0x" + "b" * 40,
91
+ from_chain=8453,
92
+ to_chain=1,
93
+ from_wallet="0x1234567890123456789012345678901234567890",
94
+ from_amount="1000000000000000000",
78
95
  )
79
96
 
80
97
  @pytest.mark.asyncio
81
98
  async def test_get_best_quote_success(self, adapter, mock_brap_client):
82
99
  """Test successful best quote retrieval"""
83
100
  mock_response = {
84
- "best_route": {
85
- "input_amount": "1000000000000000000",
86
- "output_amount": "995000000000000000",
87
- "total_fee": "8000000000000000",
88
- }
101
+ "quotes": [],
102
+ "best_quote": {
103
+ "provider": "enso",
104
+ "input_amount": 1000000000000000000,
105
+ "output_amount": 995000000000000000,
106
+ "calldata": {
107
+ "data": "0x",
108
+ "to": "0x",
109
+ "from_address": "0x",
110
+ "value": "0",
111
+ "chainId": 8453,
112
+ },
113
+ "fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
114
+ },
89
115
  }
90
116
  mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
91
117
 
@@ -99,14 +125,14 @@ class TestBRAPAdapter:
99
125
  amount="1000000000000000000",
100
126
  )
101
127
 
102
- assert success is True
103
- assert data["input_amount"] == "1000000000000000000"
104
- assert data["output_amount"] == "995000000000000000"
128
+ assert success
129
+ assert data["input_amount"] == 1000000000000000000
130
+ assert data["output_amount"] == 995000000000000000
105
131
 
106
132
  @pytest.mark.asyncio
107
133
  async def test_get_best_quote_no_quotes(self, adapter, mock_brap_client):
108
134
  """Test best quote retrieval when no quotes available"""
109
- mock_response = {"best_route": None}
135
+ mock_response = {"quotes": [], "best_quote": None}
110
136
  mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
111
137
 
112
138
  success, data = await adapter.get_best_quote(
@@ -126,18 +152,25 @@ class TestBRAPAdapter:
126
152
  async def test_calculate_swap_fees_success(self, adapter, mock_brap_client):
127
153
  """Test successful swap fee calculation"""
128
154
  mock_quote_response = {
129
- "quotes": {
130
- "best_quote": {
131
- "input_amount": "1000000000000000000",
132
- "output_amount": "995000000000000000",
133
- "gas_fee": "5000000000000000",
134
- "bridge_fee": "2000000000000000",
135
- "protocol_fee": "1000000000000000",
136
- "total_fee": "8000000000000000",
137
- "slippage": 0.01,
138
- "price_impact": 0.005,
139
- }
140
- }
155
+ "quotes": [],
156
+ "best_quote": {
157
+ "provider": "enso",
158
+ "input_amount": 1000000000000000000,
159
+ "output_amount": 995000000000000000,
160
+ "gas_estimate": 5000000000000000,
161
+ "quote": {"priceImpact": 5},
162
+ "fee_estimate": {
163
+ "fee_total_usd": 0.008,
164
+ "fee_breakdown": [],
165
+ },
166
+ "calldata": {
167
+ "data": "0x",
168
+ "to": "0x",
169
+ "from_address": "0x",
170
+ "value": "0",
171
+ "chainId": 8453,
172
+ },
173
+ },
141
174
  }
142
175
  mock_brap_client.get_quote = AsyncMock(return_value=mock_quote_response)
143
176
 
@@ -150,36 +183,54 @@ class TestBRAPAdapter:
150
183
  slippage=0.01,
151
184
  )
152
185
 
153
- assert success is True
154
- assert data["input_amount"] == "1000000000000000000"
155
- assert data["output_amount"] == "995000000000000000"
156
- assert data["gas_fee"] == "5000000000000000"
157
- assert data["total_fee"] == "8000000000000000"
186
+ assert success
187
+ assert data["input_amount"] == 1000000000000000000
188
+ assert data["output_amount"] == 995000000000000000
189
+ assert data["gas_fee"] == 5000000000000000
190
+ assert data["total_fee"] == 0.008
158
191
 
159
192
  @pytest.mark.asyncio
160
193
  async def test_compare_routes_success(self, adapter, mock_brap_client):
161
194
  """Test successful route comparison"""
162
195
  mock_response = {
163
- "quotes": {
164
- "quotes": [
165
- {
166
- "route": "Route 1",
167
- "output_amount": "995000000000000000",
168
- "total_fee": "8000000000000000",
169
- "estimated_time": 300,
196
+ "quotes": [
197
+ {
198
+ "provider": "enso",
199
+ "output_amount": 995000000000000000,
200
+ "fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
201
+ "calldata": {
202
+ "data": "0x",
203
+ "to": "0x",
204
+ "from_address": "0x",
205
+ "value": "0",
206
+ "chainId": 8453,
170
207
  },
171
- {
172
- "route": "Route 2",
173
- "output_amount": "992000000000000000",
174
- "total_fee": "12000000000000000",
175
- "estimated_time": 180,
208
+ },
209
+ {
210
+ "provider": "enso",
211
+ "output_amount": 992000000000000000,
212
+ "fee_estimate": {"fee_total_usd": 0.012, "fee_breakdown": []},
213
+ "calldata": {
214
+ "data": "0x",
215
+ "to": "0x",
216
+ "from_address": "0x",
217
+ "value": "0",
218
+ "chainId": 8453,
176
219
  },
177
- ],
178
- "best_quote": {
179
- "output_amount": "995000000000000000",
180
- "total_fee": "8000000000000000",
181
220
  },
182
- }
221
+ ],
222
+ "best_quote": {
223
+ "provider": "enso",
224
+ "output_amount": 995000000000000000,
225
+ "fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
226
+ "calldata": {
227
+ "data": "0x",
228
+ "to": "0x",
229
+ "from_address": "0x",
230
+ "value": "0",
231
+ "chainId": 8453,
232
+ },
233
+ },
183
234
  }
184
235
  mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
185
236
 
@@ -191,10 +242,10 @@ class TestBRAPAdapter:
191
242
  amount="1000000000000000000",
192
243
  )
193
244
 
194
- assert success is True
245
+ assert success
195
246
  assert data["total_routes"] == 2
196
247
  assert len(data["all_routes"]) == 2
197
- assert data["best_route"]["output_amount"] == "995000000000000000"
248
+ assert data["best_route"]["output_amount"] == 995000000000000000
198
249
 
199
250
  @pytest.mark.asyncio
200
251
  async def test_estimate_gas_cost_success(self, adapter):
@@ -203,7 +254,7 @@ class TestBRAPAdapter:
203
254
  from_chain_id=8453, to_chain_id=1, operation_type="swap"
204
255
  )
205
256
 
206
- assert success is True
257
+ assert success
207
258
  assert data["from_chain"] == "base"
208
259
  assert data["to_chain"] == "ethereum"
209
260
  assert data["from_gas_estimate"] == 100000
@@ -214,7 +265,18 @@ class TestBRAPAdapter:
214
265
  async def test_validate_swap_parameters_success(self, adapter, mock_brap_client):
215
266
  """Test successful swap parameter validation"""
216
267
  mock_quote_response = {
217
- "quotes": {"best_quote": {"output_amount": "995000000000000000"}}
268
+ "quotes": [],
269
+ "best_quote": {
270
+ "output_amount": 995000000000000000,
271
+ "calldata": {
272
+ "data": "0x",
273
+ "to": "0x",
274
+ "from_address": "0x",
275
+ "value": "0",
276
+ "chainId": 8453,
277
+ },
278
+ "fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
279
+ },
218
280
  }
219
281
  mock_brap_client.get_quote = AsyncMock(return_value=mock_quote_response)
220
282
 
@@ -226,7 +288,7 @@ class TestBRAPAdapter:
226
288
  amount="1000000000000000000",
227
289
  )
228
290
 
229
- assert success is True
291
+ assert success
230
292
  assert data["valid"] is True
231
293
  assert data["quote_available"] is True
232
294
  assert data["estimated_output"] == "995000000000000000"
@@ -10,7 +10,7 @@ from wayfinder_paths.core.clients.HyperlendClient import (
10
10
  HyperlendClient,
11
11
  LendRateHistory,
12
12
  MarketEntry,
13
- StableMarket,
13
+ StableMarketsHeadroomResponse,
14
14
  )
15
15
  from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
16
16
  from wayfinder_paths.core.constants.hyperlend_abi import (
@@ -66,55 +66,71 @@ class HyperlendAdapter(BaseAdapter):
66
66
 
67
67
  async def get_stable_markets(
68
68
  self,
69
- chain_id: int,
69
+ *,
70
70
  required_underlying_tokens: float | None = None,
71
71
  buffer_bps: int | None = None,
72
72
  min_buffer_tokens: float | None = None,
73
- is_stable_symbol: bool | None = None,
74
- ) -> tuple[bool, list[StableMarket] | str]:
73
+ ) -> tuple[bool, StableMarketsHeadroomResponse | str]:
75
74
  try:
76
75
  data = await self.hyperlend_client.get_stable_markets(
77
- chain_id=chain_id,
78
76
  required_underlying_tokens=required_underlying_tokens,
79
77
  buffer_bps=buffer_bps,
80
78
  min_buffer_tokens=min_buffer_tokens,
81
- is_stable_symbol=is_stable_symbol,
82
79
  )
80
+ # Strategies expect a dict with "markets" and "notes"; normalize if API returns a list
81
+ if isinstance(data, list):
82
+ markets: dict[str, Any] = {}
83
+ for i, item in enumerate(data):
84
+ if isinstance(item, dict) and "address" in item:
85
+ markets[item["address"]] = item
86
+ elif isinstance(item, dict):
87
+ markets[str(i)] = item
88
+ data = {"markets": markets, "notes": []}
89
+ elif isinstance(data, dict) and ("markets" not in data or "notes" not in data):
90
+ data = {
91
+ "markets": data.get("markets", {}),
92
+ "notes": data.get("notes", []),
93
+ }
83
94
  return True, data
84
95
  except Exception as exc:
85
96
  return False, str(exc)
86
97
 
87
98
  async def get_assets_view(
88
- self, chain_id: int, user_address: str
99
+ self,
100
+ *,
101
+ user_address: str,
89
102
  ) -> tuple[bool, AssetsView | str]:
90
103
  try:
91
104
  data = await self.hyperlend_client.get_assets_view(
92
- chain_id=chain_id, user_address=user_address
105
+ user_address=user_address
93
106
  )
94
107
  return True, data
95
108
  except Exception as exc:
96
109
  return False, str(exc)
97
110
 
98
111
  async def get_market_entry(
99
- self, chain_id: int, underlying: str
112
+ self,
113
+ *,
114
+ token: str,
100
115
  ) -> tuple[bool, MarketEntry | str]:
101
116
  try:
102
- data = await self.hyperlend_client.get_market_entry(chain_id, underlying)
117
+ data = await self.hyperlend_client.get_market_entry(token=token)
103
118
  return True, data
104
119
  except Exception as exc:
105
120
  return False, str(exc)
106
121
 
107
122
  async def get_lend_rate_history(
108
123
  self,
109
- chain_id: int,
110
- token_address: str,
124
+ *,
125
+ token: str,
111
126
  lookback_hours: int,
127
+ force_refresh: bool | None = None,
112
128
  ) -> tuple[bool, LendRateHistory | str]:
113
129
  try:
114
130
  data = await self.hyperlend_client.get_lend_rate_history(
115
- chain_id=chain_id,
116
- token_address=token_address,
131
+ token=token,
117
132
  lookback_hours=lookback_hours,
133
+ force_refresh=force_refresh,
118
134
  )
119
135
  return True, data
120
136
  except Exception as exc: