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
@@ -8,13 +8,15 @@ from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
8
8
  from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
9
9
  from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
10
10
  from wayfinder_paths.core.adapters.models import SWAP
11
- from wayfinder_paths.core.clients.BRAPClient import BRAPClient, BRAPQuote
11
+ from wayfinder_paths.core.clients.BRAPClient import (
12
+ BRAPClient,
13
+ BRAPQuoteResponse,
14
+ )
12
15
  from wayfinder_paths.core.clients.LedgerClient import TransactionRecord
13
16
  from wayfinder_paths.core.clients.TokenClient import TokenClient
14
17
  from wayfinder_paths.core.constants import DEFAULT_SLIPPAGE, ZERO_ADDRESS
15
18
  from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
16
19
  from wayfinder_paths.core.services.base import Web3Service
17
- from wayfinder_paths.core.settings import settings
18
20
 
19
21
  _NEEDS_CLEAR_APPROVAL = {
20
22
  (1, "0xdac17f958d2ee523a2206206994597c13d831ec7"),
@@ -62,7 +64,7 @@ class BRAPAdapter(BaseAdapter):
62
64
  amount: str,
63
65
  slippage: float | None = None,
64
66
  wayfinder_fee: float | None = None,
65
- ) -> tuple[bool, BRAPQuote | str]:
67
+ ) -> tuple[bool, BRAPQuoteResponse | str]:
66
68
  """
67
69
  Get a quote for a cross-chain swap operation.
68
70
 
@@ -78,19 +80,16 @@ class BRAPAdapter(BaseAdapter):
78
80
  wayfinder_fee: Wayfinder fee (optional)
79
81
 
80
82
  Returns:
81
- Tuple of (success, data) where data is quote information or error message
83
+ Tuple of (success, data) where data is quote response or error message
82
84
  """
83
85
  try:
84
86
  data = await self.brap_client.get_quote(
85
- from_token_address=from_token_address,
86
- to_token_address=to_token_address,
87
- from_chain_id=from_chain_id,
88
- to_chain_id=to_chain_id,
89
- from_address=from_address,
90
- to_address=to_address,
91
- amount1=amount,
92
- slippage=slippage,
93
- wayfinder_fee=wayfinder_fee,
87
+ from_token=from_token_address,
88
+ to_token=to_token_address,
89
+ from_chain=from_chain_id,
90
+ to_chain=to_chain_id,
91
+ from_wallet=from_address,
92
+ from_amount=amount,
94
93
  )
95
94
  return (True, data)
96
95
  except Exception as e:
@@ -131,22 +130,28 @@ class BRAPAdapter(BaseAdapter):
131
130
  """
132
131
  try:
133
132
  data = await self.brap_client.get_quote(
134
- from_token_address=from_token_address,
135
- to_token_address=to_token_address,
136
- from_chain_id=from_chain_id,
137
- to_chain_id=to_chain_id,
138
- from_address=from_address,
139
- to_address=to_address,
140
- amount1=amount,
141
- slippage=slippage,
142
- wayfinder_fee=wayfinder_fee,
133
+ from_token=from_token_address,
134
+ to_token=to_token_address,
135
+ from_chain=from_chain_id,
136
+ to_chain=to_chain_id,
137
+ from_wallet=from_address,
138
+ from_amount=amount,
143
139
  )
144
140
 
145
- quotes_container = data.get("quotes", {})
146
- # BRAP returns quotes in "all_quotes" not "quotes"
147
- all_quotes = quotes_container.get("all_quotes", []) or quotes_container.get(
148
- "quotes", []
149
- )
141
+ raw_quotes = data.get("quotes")
142
+ if isinstance(raw_quotes, list):
143
+ all_quotes = raw_quotes
144
+ best_quote = data.get("best_quote") or data.get("best_route")
145
+ else:
146
+ quotes_container = raw_quotes or {}
147
+ all_quotes = quotes_container.get(
148
+ "all_quotes", []
149
+ ) or quotes_container.get("quotes", [])
150
+ best_quote = (
151
+ quotes_container.get("best_quote")
152
+ or data.get("best_quote")
153
+ or data.get("best_route")
154
+ )
150
155
 
151
156
  # If preferred providers specified, select by provider preference
152
157
  if preferred_providers and all_quotes:
@@ -157,13 +162,6 @@ class BRAPAdapter(BaseAdapter):
157
162
  return (True, selected_quote)
158
163
  # Fall through to best_quote if no preferred provider found
159
164
 
160
- # Extract best quote from response - check nested quotes first, then top level
161
- best_quote = (
162
- quotes_container.get("best_quote")
163
- or data.get("best_quote")
164
- or data.get("best_route")
165
- )
166
-
167
165
  if not best_quote:
168
166
  return (False, "No quotes available")
169
167
 
@@ -254,8 +252,8 @@ class BRAPAdapter(BaseAdapter):
254
252
  to_token_address=to_token_address,
255
253
  from_chain_id=from_chain_id,
256
254
  to_chain_id=to_chain_id,
257
- from_address="0x0000000000000000000000000000000000000000", # Dummy address
258
- to_address="0x0000000000000000000000000000000000000000", # Dummy address
255
+ from_address="0x0000000000000000000000000000000000000000",
256
+ to_address="0x0000000000000000000000000000000000000000",
259
257
  amount=amount,
260
258
  slippage=slippage,
261
259
  )
@@ -263,22 +261,22 @@ class BRAPAdapter(BaseAdapter):
263
261
  if not success:
264
262
  return (False, quote_data)
265
263
 
266
- quotes = quote_data.get("quotes", {})
267
- best_quote = quotes.get("best_quote")
264
+ best_quote = quote_data.get("best_quote")
268
265
 
269
266
  if not best_quote:
270
267
  return (False, "No quote available for fee calculation")
271
268
 
272
269
  # Extract fee information
270
+ fee_estimate = best_quote.get("fee_estimate", {})
273
271
  fees = {
274
272
  "input_amount": best_quote.get("input_amount", 0),
275
273
  "output_amount": best_quote.get("output_amount", 0),
276
- "gas_fee": best_quote.get("gas_fee", 0),
277
- "bridge_fee": best_quote.get("bridge_fee", 0),
278
- "protocol_fee": best_quote.get("protocol_fee", 0),
279
- "total_fee": best_quote.get("total_fee", 0),
280
- "slippage": best_quote.get("slippage", 0),
281
- "price_impact": best_quote.get("price_impact", 0),
274
+ "gas_fee": best_quote.get("gas_estimate") or 0,
275
+ "bridge_fee": 0,
276
+ "protocol_fee": fee_estimate.get("fee_total_usd", 0),
277
+ "total_fee": fee_estimate.get("fee_total_usd", 0),
278
+ "slippage": 0,
279
+ "price_impact": best_quote.get("quote", {}).get("priceImpact", 0),
282
280
  }
283
281
 
284
282
  return (True, fees)
@@ -311,19 +309,26 @@ class BRAPAdapter(BaseAdapter):
311
309
  """
312
310
  try:
313
311
  data = await self.brap_client.get_quote(
314
- from_token_address=from_token_address,
315
- to_token_address=to_token_address,
316
- from_chain_id=from_chain_id,
317
- to_chain_id=to_chain_id,
318
- from_address="0x0000000000000000000000000000000000000000", # Dummy address
319
- to_address="0x0000000000000000000000000000000000000000", # Dummy address
320
- amount1=amount,
321
- slippage=slippage,
312
+ from_token=from_token_address,
313
+ to_token=to_token_address,
314
+ from_chain=from_chain_id,
315
+ to_chain=to_chain_id,
316
+ from_wallet="0x0000000000000000000000000000000000000000",
317
+ from_amount=amount,
322
318
  )
323
319
 
324
- quotes = data.get("quotes", {})
325
- all_quotes = quotes.get("all_quotes", []) or quotes.get("quotes", [])
326
- best_quote = quotes.get("best_quote")
320
+ raw_quotes = data.get("quotes")
321
+ if isinstance(raw_quotes, list):
322
+ all_quotes = raw_quotes
323
+ best_quote = data.get("best_quote") or data.get("best_route")
324
+ else:
325
+ quotes = raw_quotes or {}
326
+ all_quotes = quotes.get("all_quotes", []) or quotes.get("quotes", [])
327
+ best_quote = (
328
+ quotes.get("best_quote")
329
+ or data.get("best_quote")
330
+ or data.get("best_route")
331
+ )
327
332
 
328
333
  if not all_quotes:
329
334
  return (False, "No routes available")
@@ -339,16 +344,21 @@ class BRAPAdapter(BaseAdapter):
339
344
  "all_routes": sorted_quotes,
340
345
  "route_analysis": {
341
346
  "highest_output": sorted_quotes[0] if sorted_quotes else None,
342
- "lowest_fees": min(
343
- all_quotes, key=lambda x: int(x.get("total_fee", 0))
344
- )
345
- if all_quotes
346
- else None,
347
- "fastest": min(
348
- all_quotes, key=lambda x: int(x.get("estimated_time", 0))
349
- )
350
- if all_quotes
351
- else None,
347
+ "lowest_fees": (
348
+ min(
349
+ all_quotes,
350
+ key=lambda x: float(
351
+ x.get("fee_estimate", {}).get("fee_total_usd", 0)
352
+ ),
353
+ )
354
+ if all_quotes
355
+ else None
356
+ ),
357
+ "fastest": (
358
+ min(all_quotes, key=lambda x: int(x.get("estimated_time", 0)))
359
+ if all_quotes
360
+ else None
361
+ ),
352
362
  },
353
363
  }
354
364
 
@@ -423,10 +433,13 @@ class BRAPAdapter(BaseAdapter):
423
433
  chain = from_token.get("chain") or {}
424
434
  chain_id = self._chain_id(chain)
425
435
 
426
- transaction = dict(quote.get("calldata") or {})
427
- if not transaction:
436
+ calldata = quote.get("calldata") or {}
437
+ transaction = dict(calldata)
438
+ if not transaction or not transaction.get("data"):
428
439
  return (False, "Quote missing calldata")
429
440
  transaction["chainId"] = chain_id
441
+ # Always set the sender to the strategy wallet for broadcast.
442
+ # (Calldata may include either "from" or "from_address" depending on provider.)
430
443
  transaction["from"] = to_checksum_address(from_address)
431
444
 
432
445
  spender = transaction.get("to")
@@ -517,8 +530,8 @@ class BRAPAdapter(BaseAdapter):
517
530
  to_token_address=to_token_address,
518
531
  from_chain_id=from_chain_id,
519
532
  to_chain_id=to_chain_id,
520
- from_address="0x0000000000000000000000000000000000000000", # Dummy address
521
- to_address="0x0000000000000000000000000000000000000000", # Dummy address
533
+ from_address="0x0000000000000000000000000000000000000000",
534
+ to_address="0x0000000000000000000000000000000000000000",
522
535
  amount=amount,
523
536
  slippage=slippage,
524
537
  )
@@ -635,14 +648,15 @@ class BRAPAdapter(BaseAdapter):
635
648
  validation_errors.append(f"Swap not possible: {quote_data}")
636
649
  return (False, {"valid": False, "errors": validation_errors})
637
650
 
651
+ best_quote = (
652
+ quote_data.get("best_quote", {}) if isinstance(quote_data, dict) else {}
653
+ )
638
654
  return (
639
655
  True,
640
656
  {
641
657
  "valid": True,
642
658
  "quote_available": True,
643
- "estimated_output": quote_data.get("quotes", {})
644
- .get("best_quote", {})
645
- .get("output_amount", "0"),
659
+ "estimated_output": str(best_quote.get("output_amount", 0)),
646
660
  },
647
661
  )
648
662
  except Exception as e:
@@ -698,8 +712,6 @@ class BRAPAdapter(BaseAdapter):
698
712
  async def _broadcast_transaction(
699
713
  self, transaction: dict[str, Any], confirmations: int = 0
700
714
  ) -> tuple[bool, Any]:
701
- if getattr(settings, "DRY_RUN", False):
702
- return True, {"dry_run": True, "transaction": transaction}
703
715
  return await self.wallet_provider.broadcast_transaction(
704
716
  transaction,
705
717
  wait_for_receipt=True,
@@ -782,7 +794,7 @@ class BRAPAdapter(BaseAdapter):
782
794
 
783
795
  def _chain_id(self, chain: Any) -> int:
784
796
  if isinstance(chain, dict):
785
- chain_id = chain.get("id") or chain.get("chain_id")
797
+ chain_id = chain.get("id")
786
798
  else:
787
799
  chain_id = getattr(chain, "id", None)
788
800
  if chain_id is None:
@@ -14,25 +14,37 @@
14
14
  "output": {
15
15
  "success": true,
16
16
  "data": {
17
- "quotes": {
18
- "best_quote": {
19
- "input_amount": "1000000000000000000",
20
- "output_amount": "995000000000000000",
21
- "gas_fee": "5000000000000000",
22
- "bridge_fee": "2000000000000000",
23
- "protocol_fee": "1000000000000000",
24
- "total_fee": "8000000000000000",
25
- "slippage": 0.01,
26
- "price_impact": 0.005,
27
- "estimated_time": 300
28
- },
29
- "quotes": [
30
- {
31
- "route": "Route 1",
32
- "output_amount": "995000000000000000",
33
- "total_fee": "8000000000000000"
17
+ "quotes": [
18
+ {
19
+ "provider": "enso",
20
+ "input_amount": 1000000000000000000,
21
+ "output_amount": 995000000000000000,
22
+ "fee_estimate": {
23
+ "fee_total_usd": 0.08,
24
+ "fee_breakdown": []
25
+ },
26
+ "calldata": {
27
+ "data": "0x",
28
+ "to": "0x",
29
+ "value": "0",
30
+ "chainId": 8453
34
31
  }
35
- ]
32
+ }
33
+ ],
34
+ "best_quote": {
35
+ "provider": "enso",
36
+ "input_amount": 1000000000000000000,
37
+ "output_amount": 995000000000000000,
38
+ "fee_estimate": {
39
+ "fee_total_usd": 0.08,
40
+ "fee_breakdown": []
41
+ },
42
+ "calldata": {
43
+ "data": "0x",
44
+ "to": "0x",
45
+ "value": "0",
46
+ "chainId": 8453
47
+ }
36
48
  }
37
49
  }
38
50
  }
@@ -52,15 +64,19 @@
52
64
  "output": {
53
65
  "success": true,
54
66
  "data": {
55
- "input_amount": "1000000000000000000",
56
- "output_amount": "995000000000000000",
57
- "gas_fee": "5000000000000000",
58
- "bridge_fee": "2000000000000000",
59
- "protocol_fee": "1000000000000000",
60
- "total_fee": "8000000000000000",
61
- "slippage": 0.01,
62
- "price_impact": 0.005,
63
- "estimated_time": 300
67
+ "provider": "enso",
68
+ "input_amount": 1000000000000000000,
69
+ "output_amount": 995000000000000000,
70
+ "fee_estimate": {
71
+ "fee_total_usd": 0.08,
72
+ "fee_breakdown": []
73
+ },
74
+ "calldata": {
75
+ "data": "0x",
76
+ "to": "0x",
77
+ "value": "0",
78
+ "chainId": 8453
79
+ }
64
80
  }
65
81
  }
66
82
  },
@@ -77,14 +93,14 @@
77
93
  "output": {
78
94
  "success": true,
79
95
  "data": {
80
- "input_amount": "1000000000000000000",
81
- "output_amount": "995000000000000000",
82
- "gas_fee": "5000000000000000",
83
- "bridge_fee": "2000000000000000",
84
- "protocol_fee": "1000000000000000",
85
- "total_fee": "8000000000000000",
86
- "slippage": 0.01,
87
- "price_impact": 0.005
96
+ "input_amount": 1000000000000000000,
97
+ "output_amount": 995000000000000000,
98
+ "gas_fee": 567840,
99
+ "bridge_fee": 0,
100
+ "protocol_fee": 0.08,
101
+ "total_fee": 0.08,
102
+ "slippage": 0,
103
+ "price_impact": 6888
88
104
  }
89
105
  }
90
106
  },
@@ -101,35 +117,30 @@
101
117
  "output": {
102
118
  "success": true,
103
119
  "data": {
104
- "total_routes": 3,
120
+ "total_routes": 2,
105
121
  "best_route": {
106
- "input_amount": "1000000000000000000",
107
- "output_amount": "995000000000000000",
108
- "total_fee": "8000000000000000"
122
+ "provider": "enso",
123
+ "output_amount": 995000000000000000,
124
+ "fee_estimate": { "fee_total_usd": 0.08, "fee_breakdown": [] }
109
125
  },
110
126
  "all_routes": [
111
127
  {
112
- "route": "Route 1",
113
- "output_amount": "995000000000000000",
114
- "total_fee": "8000000000000000",
115
- "estimated_time": 300
128
+ "provider": "enso",
129
+ "output_amount": 995000000000000000,
130
+ "fee_estimate": { "fee_total_usd": 0.08, "fee_breakdown": [] }
116
131
  },
117
132
  {
118
- "route": "Route 2",
119
- "output_amount": "992000000000000000",
120
- "total_fee": "12000000000000000",
121
- "estimated_time": 180
133
+ "provider": "enso",
134
+ "output_amount": 992000000000000000,
135
+ "fee_estimate": { "fee_total_usd": 0.12, "fee_breakdown": [] }
122
136
  }
123
137
  ],
124
138
  "route_analysis": {
125
139
  "highest_output": {
126
- "output_amount": "995000000000000000"
140
+ "output_amount": 995000000000000000
127
141
  },
128
142
  "lowest_fees": {
129
- "total_fee": "8000000000000000"
130
- },
131
- "fastest": {
132
- "estimated_time": 180
143
+ "fee_estimate": { "fee_total_usd": 0.08, "fee_breakdown": [] }
133
144
  }
134
145
  }
135
146
  }