wayfinder-paths 0.1.11__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 (66) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +13 -14
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +36 -39
  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 +87 -75
  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 +22 -23
  9. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
  10. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +1 -1
  11. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +44 -5
  12. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +104 -0
  13. wayfinder_paths/adapters/moonwell_adapter/adapter.py +0 -3
  14. wayfinder_paths/adapters/pool_adapter/README.md +11 -27
  15. wayfinder_paths/adapters/pool_adapter/adapter.py +11 -37
  16. wayfinder_paths/adapters/pool_adapter/examples.json +6 -7
  17. wayfinder_paths/adapters/pool_adapter/test_adapter.py +8 -8
  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 +5 -3
  22. wayfinder_paths/core/clients/BRAPClient.py +103 -62
  23. wayfinder_paths/core/clients/ClientManager.py +1 -68
  24. wayfinder_paths/core/clients/HyperlendClient.py +127 -66
  25. wayfinder_paths/core/clients/LedgerClient.py +1 -4
  26. wayfinder_paths/core/clients/PoolClient.py +126 -88
  27. wayfinder_paths/core/clients/TokenClient.py +92 -37
  28. wayfinder_paths/core/clients/WalletClient.py +28 -58
  29. wayfinder_paths/core/clients/WayfinderClient.py +33 -166
  30. wayfinder_paths/core/clients/__init__.py +0 -2
  31. wayfinder_paths/core/clients/protocols.py +35 -52
  32. wayfinder_paths/core/clients/sdk_example.py +37 -22
  33. wayfinder_paths/core/config.py +60 -224
  34. wayfinder_paths/core/engine/StrategyJob.py +7 -55
  35. wayfinder_paths/core/services/local_evm_txn.py +28 -10
  36. wayfinder_paths/core/services/local_token_txn.py +1 -1
  37. wayfinder_paths/core/strategies/Strategy.py +3 -5
  38. wayfinder_paths/core/strategies/descriptors.py +7 -0
  39. wayfinder_paths/core/utils/evm_helpers.py +7 -3
  40. wayfinder_paths/core/utils/wallets.py +12 -19
  41. wayfinder_paths/core/wallets/README.md +1 -1
  42. wayfinder_paths/run_strategy.py +8 -17
  43. wayfinder_paths/scripts/create_strategy.py +5 -5
  44. wayfinder_paths/scripts/make_wallets.py +5 -5
  45. wayfinder_paths/scripts/run_strategy.py +3 -3
  46. wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1 -1
  47. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +206 -526
  48. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +228 -11
  49. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +2 -2
  50. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +41 -25
  51. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
  52. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +1 -1
  53. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +10 -9
  54. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
  55. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +3 -3
  56. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +110 -78
  57. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +44 -21
  58. wayfinder_paths/templates/adapter/README.md +1 -1
  59. wayfinder_paths/templates/strategy/README.md +3 -3
  60. wayfinder_paths/templates/strategy/test_strategy.py +3 -2
  61. {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +21 -59
  62. {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +64 -65
  63. wayfinder_paths/core/clients/AuthClient.py +0 -83
  64. wayfinder_paths/core/settings.py +0 -61
  65. {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
  66. {wayfinder_paths-0.1.11.dist-info → wayfinder_paths-0.1.14.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
 
@@ -66,26 +86,32 @@ class TestBRAPAdapter:
66
86
  assert success is True
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
 
@@ -100,13 +126,13 @@ class TestBRAPAdapter:
100
126
  )
101
127
 
102
128
  assert success is True
103
- assert data["input_amount"] == "1000000000000000000"
104
- assert data["output_amount"] == "995000000000000000"
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
 
@@ -151,35 +184,53 @@ class TestBRAPAdapter:
151
184
  )
152
185
 
153
186
  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"
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
 
@@ -194,7 +245,7 @@ class TestBRAPAdapter:
194
245
  assert success is True
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):
@@ -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
 
@@ -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 (
@@ -18,7 +18,6 @@ from wayfinder_paths.core.constants.hyperlend_abi import (
18
18
  WRAPPED_TOKEN_GATEWAY_ABI,
19
19
  )
20
20
  from wayfinder_paths.core.services.base import Web3Service
21
- from wayfinder_paths.core.settings import settings
22
21
 
23
22
  HYPERLEND_DEFAULTS = {
24
23
  "pool": "0x00A89d7a5A02160f20150EbEA7a2b5E4879A1A8b",
@@ -67,55 +66,57 @@ class HyperlendAdapter(BaseAdapter):
67
66
 
68
67
  async def get_stable_markets(
69
68
  self,
70
- chain_id: int,
69
+ *,
71
70
  required_underlying_tokens: float | None = None,
72
71
  buffer_bps: int | None = None,
73
72
  min_buffer_tokens: float | None = None,
74
- is_stable_symbol: bool | None = None,
75
- ) -> tuple[bool, list[StableMarket] | str]:
73
+ ) -> tuple[bool, StableMarketsHeadroomResponse | str]:
76
74
  try:
77
75
  data = await self.hyperlend_client.get_stable_markets(
78
- chain_id=chain_id,
79
76
  required_underlying_tokens=required_underlying_tokens,
80
77
  buffer_bps=buffer_bps,
81
78
  min_buffer_tokens=min_buffer_tokens,
82
- is_stable_symbol=is_stable_symbol,
83
79
  )
84
80
  return True, data
85
81
  except Exception as exc:
86
82
  return False, str(exc)
87
83
 
88
84
  async def get_assets_view(
89
- self, chain_id: int, user_address: str
85
+ self,
86
+ *,
87
+ user_address: str,
90
88
  ) -> tuple[bool, AssetsView | str]:
91
89
  try:
92
90
  data = await self.hyperlend_client.get_assets_view(
93
- chain_id=chain_id, user_address=user_address
91
+ user_address=user_address
94
92
  )
95
93
  return True, data
96
94
  except Exception as exc:
97
95
  return False, str(exc)
98
96
 
99
97
  async def get_market_entry(
100
- self, chain_id: int, underlying: str
98
+ self,
99
+ *,
100
+ token: str,
101
101
  ) -> tuple[bool, MarketEntry | str]:
102
102
  try:
103
- data = await self.hyperlend_client.get_market_entry(chain_id, underlying)
103
+ data = await self.hyperlend_client.get_market_entry(token=token)
104
104
  return True, data
105
105
  except Exception as exc:
106
106
  return False, str(exc)
107
107
 
108
108
  async def get_lend_rate_history(
109
109
  self,
110
- chain_id: int,
111
- token_address: str,
110
+ *,
111
+ token: str,
112
112
  lookback_hours: int,
113
+ force_refresh: bool | None = None,
113
114
  ) -> tuple[bool, LendRateHistory | str]:
114
115
  try:
115
116
  data = await self.hyperlend_client.get_lend_rate_history(
116
- chain_id=chain_id,
117
- token_address=token_address,
117
+ token=token,
118
118
  lookback_hours=lookback_hours,
119
+ force_refresh=force_refresh,
119
120
  )
120
121
  return True, data
121
122
  except Exception as exc:
@@ -136,7 +137,7 @@ class HyperlendAdapter(BaseAdapter):
136
137
  chain_id = int(chain_id)
137
138
 
138
139
  if native:
139
- tx = self._encode_call(
140
+ tx = await self._encode_call(
140
141
  target=self.gateway_address,
141
142
  abi=WRAPPED_TOKEN_GATEWAY_ABI,
142
143
  fn_name="depositETH",
@@ -156,7 +157,7 @@ class HyperlendAdapter(BaseAdapter):
156
157
  )
157
158
  if not approved[0]:
158
159
  return approved
159
- tx = self._encode_call(
160
+ tx = await self._encode_call(
160
161
  target=self.pool_address,
161
162
  abi=POOL_ABI,
162
163
  fn_name="supply",
@@ -181,7 +182,7 @@ class HyperlendAdapter(BaseAdapter):
181
182
  chain_id = int(chain_id)
182
183
 
183
184
  if native:
184
- tx = self._encode_call(
185
+ tx = await self._encode_call(
185
186
  target=self.gateway_address,
186
187
  abi=WRAPPED_TOKEN_GATEWAY_ABI,
187
188
  fn_name="withdrawETH",
@@ -191,7 +192,7 @@ class HyperlendAdapter(BaseAdapter):
191
192
  )
192
193
  else:
193
194
  token_addr = self._checksum(underlying_token)
194
- tx = self._encode_call(
195
+ tx = await self._encode_call(
195
196
  target=self.pool_address,
196
197
  abi=POOL_ABI,
197
198
  fn_name="withdraw",
@@ -237,13 +238,11 @@ class HyperlendAdapter(BaseAdapter):
237
238
  )
238
239
 
239
240
  async def _broadcast_transaction(self, tx: dict[str, Any]) -> tuple[bool, Any]:
240
- if getattr(settings, "DRY_RUN", False):
241
- return True, {"dry_run": True, "transaction": tx}
242
241
  return await self.web3.evm_transactions.broadcast_transaction(
243
242
  tx, wait_for_receipt=True, timeout=DEFAULT_TRANSACTION_TIMEOUT
244
243
  )
245
244
 
246
- def _encode_call(
245
+ async def _encode_call(
247
246
  self,
248
247
  *,
249
248
  target: str,
@@ -258,7 +257,7 @@ class HyperlendAdapter(BaseAdapter):
258
257
  web3 = self.web3.get_web3(chain_id)
259
258
  contract = web3.eth.contract(address=target, abi=abi)
260
259
  try:
261
- data = getattr(contract.functions, fn_name)(*args).build_transaction(
260
+ data = await getattr(contract.functions, fn_name)(*args).build_transaction(
262
261
  {"from": from_address}
263
262
  )["data"]
264
263
  except ValueError as exc: