wayfinder-paths 0.1.19__py3-none-any.whl → 0.1.20__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.
- wayfinder_paths/__init__.py +0 -2
- wayfinder_paths/adapters/balance_adapter/README.md +59 -45
- wayfinder_paths/adapters/balance_adapter/adapter.py +0 -21
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -14
- wayfinder_paths/adapters/brap_adapter/README.md +61 -184
- wayfinder_paths/adapters/brap_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/brap_adapter/adapter.py +0 -147
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +0 -15
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -9
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +0 -17
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +3 -312
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +1 -71
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +0 -57
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +0 -17
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +2 -42
- wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +1 -9
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +15 -47
- wayfinder_paths/adapters/hyperliquid_adapter/utils.py +0 -7
- wayfinder_paths/adapters/ledger_adapter/README.md +54 -74
- wayfinder_paths/adapters/ledger_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/ledger_adapter/adapter.py +0 -106
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +0 -12
- wayfinder_paths/adapters/moonwell_adapter/README.md +67 -106
- wayfinder_paths/adapters/moonwell_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +9 -121
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +84 -83
- wayfinder_paths/adapters/pool_adapter/README.md +30 -51
- wayfinder_paths/adapters/pool_adapter/__init__.py +0 -4
- wayfinder_paths/adapters/pool_adapter/adapter.py +0 -19
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -8
- wayfinder_paths/adapters/token_adapter/README.md +41 -49
- wayfinder_paths/adapters/token_adapter/adapter.py +0 -32
- wayfinder_paths/adapters/token_adapter/test_adapter.py +1 -12
- wayfinder_paths/conftest.py +0 -8
- wayfinder_paths/core/__init__.py +0 -2
- wayfinder_paths/core/adapters/BaseAdapter.py +0 -22
- wayfinder_paths/core/adapters/__init__.py +0 -5
- wayfinder_paths/core/adapters/models.py +0 -5
- wayfinder_paths/core/analytics/__init__.py +0 -2
- wayfinder_paths/core/analytics/bootstrap.py +0 -16
- wayfinder_paths/core/analytics/stats.py +0 -7
- wayfinder_paths/core/analytics/test_analytics.py +5 -34
- wayfinder_paths/core/clients/BRAPClient.py +0 -35
- wayfinder_paths/core/clients/ClientManager.py +0 -51
- wayfinder_paths/core/clients/HyperlendClient.py +0 -77
- wayfinder_paths/core/clients/LedgerClient.py +2 -122
- wayfinder_paths/core/clients/PoolClient.py +0 -2
- wayfinder_paths/core/clients/TokenClient.py +0 -39
- wayfinder_paths/core/clients/WalletClient.py +0 -15
- wayfinder_paths/core/clients/WayfinderClient.py +0 -24
- wayfinder_paths/core/clients/__init__.py +0 -4
- wayfinder_paths/core/clients/protocols.py +25 -98
- wayfinder_paths/core/config.py +0 -24
- wayfinder_paths/core/constants/__init__.py +0 -7
- wayfinder_paths/core/constants/base.py +2 -9
- wayfinder_paths/core/constants/erc20_abi.py +0 -5
- wayfinder_paths/core/constants/hyperlend_abi.py +0 -7
- wayfinder_paths/core/constants/moonwell_abi.py +0 -35
- wayfinder_paths/core/engine/StrategyJob.py +0 -32
- wayfinder_paths/core/strategies/Strategy.py +0 -99
- wayfinder_paths/core/strategies/__init__.py +0 -2
- wayfinder_paths/core/utils/__init__.py +0 -1
- wayfinder_paths/core/utils/erc20_service.py +0 -1
- wayfinder_paths/core/utils/evm_helpers.py +0 -50
- wayfinder_paths/core/utils/transaction.py +0 -1
- wayfinder_paths/run_strategy.py +0 -46
- wayfinder_paths/scripts/create_strategy.py +0 -17
- wayfinder_paths/scripts/make_wallets.py +1 -4
- wayfinder_paths/strategies/basis_trading_strategy/README.md +71 -163
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +0 -24
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +36 -400
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +15 -64
- wayfinder_paths/strategies/basis_trading_strategy/types.py +0 -4
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +65 -56
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +4 -27
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -10
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +71 -72
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +23 -227
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +120 -113
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +64 -59
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +4 -44
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +2 -35
- wayfinder_paths/templates/adapter/README.md +107 -46
- wayfinder_paths/templates/adapter/adapter.py +0 -9
- wayfinder_paths/templates/adapter/test_adapter.py +0 -19
- wayfinder_paths/templates/strategy/README.md +113 -59
- wayfinder_paths/templates/strategy/strategy.py +0 -22
- wayfinder_paths/templates/strategy/test_strategy.py +0 -28
- wayfinder_paths/tests/test_test_coverage.py +2 -12
- wayfinder_paths/tests/test_utils.py +1 -31
- wayfinder_paths-0.1.20.dist-info/METADATA +355 -0
- wayfinder_paths-0.1.20.dist-info/RECORD +129 -0
- wayfinder_paths/core/adapters/base.py +0 -5
- wayfinder_paths-0.1.19.dist-info/METADATA +0 -592
- wayfinder_paths-0.1.19.dist-info/RECORD +0 -130
- {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.20.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.19.dist-info → wayfinder_paths-0.1.20.dist-info}/WHEEL +0 -0
|
@@ -29,26 +29,14 @@ _NEEDS_CLEAR_APPROVAL = {
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class BRAPAdapter(BaseAdapter):
|
|
32
|
-
"""
|
|
33
|
-
BRAP (Bridge/Router/Adapter Protocol) adapter for cross-chain swaps and quotes.
|
|
34
|
-
|
|
35
|
-
Provides high-level operations for:
|
|
36
|
-
- Getting swap quotes across chains
|
|
37
|
-
- Executing cross-chain transactions
|
|
38
|
-
- Route optimization and fee calculation
|
|
39
|
-
- Bridge operations
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
32
|
adapter_type: str = "BRAP"
|
|
43
33
|
|
|
44
34
|
def __init__(
|
|
45
35
|
self,
|
|
46
36
|
config: dict[str, Any] | None = None,
|
|
47
|
-
simulation: bool = False,
|
|
48
37
|
strategy_wallet_signing_callback=None,
|
|
49
38
|
):
|
|
50
39
|
super().__init__("brap_adapter", config)
|
|
51
|
-
self.simulation = simulation
|
|
52
40
|
self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
|
|
53
41
|
self.brap_client = BRAPClient()
|
|
54
42
|
self.token_client = TokenClient()
|
|
@@ -67,23 +55,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
67
55
|
slippage: float | None = None,
|
|
68
56
|
wayfinder_fee: float | None = None,
|
|
69
57
|
) -> tuple[bool, BRAPQuoteResponse | str]:
|
|
70
|
-
"""
|
|
71
|
-
Get a quote for a cross-chain swap operation.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
from_token_address: Source token contract address
|
|
75
|
-
to_token_address: Destination token contract address
|
|
76
|
-
from_chain_id: Source chain ID
|
|
77
|
-
to_chain_id: Destination chain ID
|
|
78
|
-
from_address: Source wallet address
|
|
79
|
-
to_address: Destination wallet address
|
|
80
|
-
amount: Amount to swap (in smallest units)
|
|
81
|
-
slippage: Maximum slippage tolerance (optional)
|
|
82
|
-
wayfinder_fee: Wayfinder fee (optional)
|
|
83
|
-
|
|
84
|
-
Returns:
|
|
85
|
-
Tuple of (success, data) where data is quote response or error message
|
|
86
|
-
"""
|
|
87
58
|
try:
|
|
88
59
|
data = await self.brap_client.get_quote(
|
|
89
60
|
from_token=from_token_address,
|
|
@@ -111,25 +82,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
111
82
|
wayfinder_fee: float | None = None,
|
|
112
83
|
preferred_providers: list[str] | None = None,
|
|
113
84
|
) -> tuple[bool, dict[str, Any] | str]:
|
|
114
|
-
"""
|
|
115
|
-
Get the best available quote for a swap operation.
|
|
116
|
-
|
|
117
|
-
Args:
|
|
118
|
-
from_token_address: Source token contract address
|
|
119
|
-
to_token_address: Destination token contract address
|
|
120
|
-
from_chain_id: Source chain ID
|
|
121
|
-
to_chain_id: Destination chain ID
|
|
122
|
-
from_address: Source wallet address
|
|
123
|
-
to_address: Destination wallet address
|
|
124
|
-
amount: Amount to swap (in smallest units)
|
|
125
|
-
slippage: Maximum slippage tolerance (optional)
|
|
126
|
-
wayfinder_fee: Wayfinder fee (optional)
|
|
127
|
-
preferred_providers: List of provider names in order of preference (optional).
|
|
128
|
-
If provided, selects first matching provider instead of "best".
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
Tuple of (success, data) where data is best quote or error message
|
|
132
|
-
"""
|
|
133
85
|
try:
|
|
134
86
|
data = await self.brap_client.get_quote(
|
|
135
87
|
from_token=from_token_address,
|
|
@@ -177,20 +129,9 @@ class BRAPAdapter(BaseAdapter):
|
|
|
177
129
|
quotes: list[dict[str, Any]],
|
|
178
130
|
preferred_providers: list[str],
|
|
179
131
|
) -> dict[str, Any] | None:
|
|
180
|
-
"""
|
|
181
|
-
Select a quote from the list based on provider preference order.
|
|
182
|
-
|
|
183
|
-
Args:
|
|
184
|
-
quotes: List of quote objects from BRAP API
|
|
185
|
-
preferred_providers: List of provider names in order of preference (case-insensitive)
|
|
186
|
-
|
|
187
|
-
Returns:
|
|
188
|
-
The first matching quote, or None if no preferred provider found
|
|
189
|
-
"""
|
|
190
132
|
# Normalize preferred providers to lowercase for case-insensitive matching
|
|
191
133
|
preferred_lower = [p.lower() for p in preferred_providers]
|
|
192
134
|
|
|
193
|
-
# Build a map of provider name -> quotes
|
|
194
135
|
provider_quotes: dict[str, list[dict[str, Any]]] = {}
|
|
195
136
|
for quote in quotes:
|
|
196
137
|
# Provider name might be in different fields depending on BRAP response structure
|
|
@@ -209,7 +150,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
209
150
|
# Select first matching provider in preference order
|
|
210
151
|
for pref in preferred_lower:
|
|
211
152
|
if pref in provider_quotes:
|
|
212
|
-
# Return the quote with highest output for this provider
|
|
213
153
|
provider_list = provider_quotes[pref]
|
|
214
154
|
best_for_provider = max(
|
|
215
155
|
provider_list, key=lambda q: int(q.get("output_amount", 0) or 0)
|
|
@@ -233,22 +173,7 @@ class BRAPAdapter(BaseAdapter):
|
|
|
233
173
|
amount: str,
|
|
234
174
|
slippage: float | None = None,
|
|
235
175
|
) -> tuple[bool, Any]:
|
|
236
|
-
"""
|
|
237
|
-
Calculate fees for a swap operation.
|
|
238
|
-
|
|
239
|
-
Args:
|
|
240
|
-
from_token_address: Source token contract address
|
|
241
|
-
to_token_address: Destination token contract address
|
|
242
|
-
from_chain_id: Source chain ID
|
|
243
|
-
to_chain_id: Destination chain ID
|
|
244
|
-
amount: Amount to swap (in smallest units)
|
|
245
|
-
slippage: Maximum slippage tolerance (optional)
|
|
246
|
-
|
|
247
|
-
Returns:
|
|
248
|
-
Tuple of (success, data) where data is fee breakdown or error message
|
|
249
|
-
"""
|
|
250
176
|
try:
|
|
251
|
-
# Get quote to extract fee information
|
|
252
177
|
success, quote_data = await self.get_swap_quote(
|
|
253
178
|
from_token_address=from_token_address,
|
|
254
179
|
to_token_address=to_token_address,
|
|
@@ -268,7 +193,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
268
193
|
if not best_quote:
|
|
269
194
|
return (False, "No quote available for fee calculation")
|
|
270
195
|
|
|
271
|
-
# Extract fee information
|
|
272
196
|
fee_estimate = best_quote.get("fee_estimate", {})
|
|
273
197
|
fees = {
|
|
274
198
|
"input_amount": best_quote.get("input_amount", 0),
|
|
@@ -295,20 +219,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
295
219
|
amount: str,
|
|
296
220
|
slippage: float | None = None,
|
|
297
221
|
) -> tuple[bool, Any]:
|
|
298
|
-
"""
|
|
299
|
-
Compare multiple routes for a swap operation.
|
|
300
|
-
|
|
301
|
-
Args:
|
|
302
|
-
from_token_address: Source token contract address
|
|
303
|
-
to_token_address: Destination token contract address
|
|
304
|
-
from_chain_id: Source chain ID
|
|
305
|
-
to_chain_id: Destination chain ID
|
|
306
|
-
amount: Amount to swap (in smallest units)
|
|
307
|
-
slippage: Maximum slippage tolerance (optional)
|
|
308
|
-
|
|
309
|
-
Returns:
|
|
310
|
-
Tuple of (success, data) where data is route comparison or error message
|
|
311
|
-
"""
|
|
312
222
|
try:
|
|
313
223
|
data = await self.brap_client.get_quote(
|
|
314
224
|
from_token=from_token_address,
|
|
@@ -379,19 +289,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
379
289
|
strategy_name: str | None = None,
|
|
380
290
|
preferred_providers: list[str] | None = None,
|
|
381
291
|
) -> tuple[bool, Any]:
|
|
382
|
-
"""
|
|
383
|
-
Execute a swap by looking up token metadata via token IDs.
|
|
384
|
-
|
|
385
|
-
Args:
|
|
386
|
-
from_token_id: Source token ID
|
|
387
|
-
to_token_id: Destination token ID
|
|
388
|
-
from_address: Wallet address
|
|
389
|
-
amount: Amount to swap (in smallest units)
|
|
390
|
-
slippage: Maximum slippage tolerance
|
|
391
|
-
strategy_name: Strategy name for ledger recording
|
|
392
|
-
preferred_providers: List of provider names in order of preference (e.g., ["enso", "aerodrome"]).
|
|
393
|
-
If provided, selects first matching provider instead of "best".
|
|
394
|
-
"""
|
|
395
292
|
from_token = await self.token_client.get_token_details(from_token_id)
|
|
396
293
|
if not from_token:
|
|
397
294
|
return (False, f"From token not found: {from_token_id}")
|
|
@@ -429,9 +326,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
429
326
|
quote: dict[str, Any],
|
|
430
327
|
strategy_name: str | None = None,
|
|
431
328
|
) -> tuple[bool, Any]:
|
|
432
|
-
"""
|
|
433
|
-
Execute a swap using a previously retrieved BRAP quote.
|
|
434
|
-
"""
|
|
435
329
|
chain = from_token.get("chain") or {}
|
|
436
330
|
chain_id = self._chain_id(chain)
|
|
437
331
|
|
|
@@ -556,20 +450,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
556
450
|
amount: str,
|
|
557
451
|
slippage: float | None = None,
|
|
558
452
|
) -> tuple[bool, Any]:
|
|
559
|
-
"""
|
|
560
|
-
Get a quote for a bridge operation (same as swap for BRAP).
|
|
561
|
-
|
|
562
|
-
Args:
|
|
563
|
-
from_token_address: Source token contract address
|
|
564
|
-
to_token_address: Destination token contract address
|
|
565
|
-
from_chain_id: Source chain ID
|
|
566
|
-
to_chain_id: Destination chain ID
|
|
567
|
-
amount: Amount to bridge (in smallest units)
|
|
568
|
-
slippage: Maximum slippage tolerance (optional)
|
|
569
|
-
|
|
570
|
-
Returns:
|
|
571
|
-
Tuple of (success, data) where data is bridge quote or error message
|
|
572
|
-
"""
|
|
573
453
|
# For BRAP, bridge operations are the same as swap operations
|
|
574
454
|
return await self.get_swap_quote(
|
|
575
455
|
from_token_address=from_token_address,
|
|
@@ -585,17 +465,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
585
465
|
async def estimate_gas_cost(
|
|
586
466
|
self, from_chain_id: int, to_chain_id: int, operation_type: str = "swap"
|
|
587
467
|
) -> tuple[bool, Any]:
|
|
588
|
-
"""
|
|
589
|
-
Estimate gas costs for a cross-chain operation.
|
|
590
|
-
|
|
591
|
-
Args:
|
|
592
|
-
from_chain_id: Source chain ID
|
|
593
|
-
to_chain_id: Destination chain ID
|
|
594
|
-
operation_type: Type of operation ("swap", "bridge")
|
|
595
|
-
|
|
596
|
-
Returns:
|
|
597
|
-
Tuple of (success, data) where data is gas cost estimate or error message
|
|
598
|
-
"""
|
|
599
468
|
try:
|
|
600
469
|
# This is a simplified estimation - in practice, you'd want to
|
|
601
470
|
# query actual gas prices from the chains
|
|
@@ -643,19 +512,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
643
512
|
to_chain_id: int,
|
|
644
513
|
amount: str,
|
|
645
514
|
) -> tuple[bool, Any]:
|
|
646
|
-
"""
|
|
647
|
-
Validate swap parameters before executing.
|
|
648
|
-
|
|
649
|
-
Args:
|
|
650
|
-
from_token_address: Source token contract address
|
|
651
|
-
to_token_address: Destination token contract address
|
|
652
|
-
from_chain_id: Source chain ID
|
|
653
|
-
to_chain_id: Destination chain ID
|
|
654
|
-
amount: Amount to swap (in smallest units)
|
|
655
|
-
|
|
656
|
-
Returns:
|
|
657
|
-
Tuple of (success, data) where data is validation result or error message
|
|
658
|
-
"""
|
|
659
515
|
try:
|
|
660
516
|
validation_errors = []
|
|
661
517
|
|
|
@@ -752,9 +608,6 @@ class BRAPAdapter(BaseAdapter):
|
|
|
752
608
|
return await self._send_tx(approve_tx)
|
|
753
609
|
|
|
754
610
|
async def _send_tx(self, tx: dict[str, Any]) -> tuple[bool, Any]:
|
|
755
|
-
"""Send transaction with simulation check."""
|
|
756
|
-
if self.simulation:
|
|
757
|
-
return True, {"simulation": tx}
|
|
758
611
|
txn_hash = await send_transaction(tx, self.strategy_wallet_signing_callback)
|
|
759
612
|
return True, txn_hash
|
|
760
613
|
|
|
@@ -6,24 +6,19 @@ from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TestBRAPAdapter:
|
|
9
|
-
"""Test cases for BRAPAdapter"""
|
|
10
|
-
|
|
11
9
|
@pytest.fixture
|
|
12
10
|
def mock_brap_client(self):
|
|
13
|
-
"""Mock BRAPClient for testing"""
|
|
14
11
|
mock_client = AsyncMock()
|
|
15
12
|
return mock_client
|
|
16
13
|
|
|
17
14
|
@pytest.fixture
|
|
18
15
|
def adapter(self, mock_brap_client):
|
|
19
|
-
"""Create a BRAPAdapter instance with mocked client for testing"""
|
|
20
16
|
adapter = BRAPAdapter()
|
|
21
17
|
adapter.brap_client = mock_brap_client
|
|
22
18
|
return adapter
|
|
23
19
|
|
|
24
20
|
@pytest.mark.asyncio
|
|
25
21
|
async def test_get_swap_quote_success(self, adapter, mock_brap_client):
|
|
26
|
-
"""Test successful swap quote retrieval"""
|
|
27
22
|
mock_response = {
|
|
28
23
|
"quotes": [
|
|
29
24
|
{
|
|
@@ -80,7 +75,6 @@ class TestBRAPAdapter:
|
|
|
80
75
|
|
|
81
76
|
@pytest.mark.asyncio
|
|
82
77
|
async def test_get_best_quote_success(self, adapter, mock_brap_client):
|
|
83
|
-
"""Test successful best quote retrieval"""
|
|
84
78
|
mock_response = {
|
|
85
79
|
"quotes": [],
|
|
86
80
|
"best_quote": {
|
|
@@ -115,7 +109,6 @@ class TestBRAPAdapter:
|
|
|
115
109
|
|
|
116
110
|
@pytest.mark.asyncio
|
|
117
111
|
async def test_get_best_quote_no_quotes(self, adapter, mock_brap_client):
|
|
118
|
-
"""Test best quote retrieval when no quotes available"""
|
|
119
112
|
mock_response = {"quotes": [], "best_quote": None}
|
|
120
113
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
|
|
121
114
|
|
|
@@ -134,7 +127,6 @@ class TestBRAPAdapter:
|
|
|
134
127
|
|
|
135
128
|
@pytest.mark.asyncio
|
|
136
129
|
async def test_calculate_swap_fees_success(self, adapter, mock_brap_client):
|
|
137
|
-
"""Test successful swap fee calculation"""
|
|
138
130
|
mock_quote_response = {
|
|
139
131
|
"quotes": [],
|
|
140
132
|
"best_quote": {
|
|
@@ -175,7 +167,6 @@ class TestBRAPAdapter:
|
|
|
175
167
|
|
|
176
168
|
@pytest.mark.asyncio
|
|
177
169
|
async def test_compare_routes_success(self, adapter, mock_brap_client):
|
|
178
|
-
"""Test successful route comparison"""
|
|
179
170
|
mock_response = {
|
|
180
171
|
"quotes": [
|
|
181
172
|
{
|
|
@@ -233,7 +224,6 @@ class TestBRAPAdapter:
|
|
|
233
224
|
|
|
234
225
|
@pytest.mark.asyncio
|
|
235
226
|
async def test_estimate_gas_cost_success(self, adapter):
|
|
236
|
-
"""Test successful gas cost estimation"""
|
|
237
227
|
success, data = await adapter.estimate_gas_cost(
|
|
238
228
|
from_chain_id=8453, to_chain_id=1, operation_type="swap"
|
|
239
229
|
)
|
|
@@ -247,7 +237,6 @@ class TestBRAPAdapter:
|
|
|
247
237
|
|
|
248
238
|
@pytest.mark.asyncio
|
|
249
239
|
async def test_validate_swap_parameters_success(self, adapter, mock_brap_client):
|
|
250
|
-
"""Test successful swap parameter validation"""
|
|
251
240
|
mock_quote_response = {
|
|
252
241
|
"quotes": [],
|
|
253
242
|
"best_quote": {
|
|
@@ -279,7 +268,6 @@ class TestBRAPAdapter:
|
|
|
279
268
|
|
|
280
269
|
@pytest.mark.asyncio
|
|
281
270
|
async def test_validate_swap_parameters_invalid_address(self, adapter):
|
|
282
|
-
"""Test swap parameter validation with invalid address"""
|
|
283
271
|
success, data = await adapter.validate_swap_parameters(
|
|
284
272
|
from_token_address="invalid_address",
|
|
285
273
|
to_token_address="0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
|
|
@@ -294,7 +282,6 @@ class TestBRAPAdapter:
|
|
|
294
282
|
|
|
295
283
|
@pytest.mark.asyncio
|
|
296
284
|
async def test_validate_swap_parameters_invalid_amount(self, adapter):
|
|
297
|
-
"""Test swap parameter validation with invalid amount"""
|
|
298
285
|
success, data = await adapter.validate_swap_parameters(
|
|
299
286
|
from_token_address="0x" + "a" * 40,
|
|
300
287
|
to_token_address="0x" + "b" * 40,
|
|
@@ -309,7 +296,6 @@ class TestBRAPAdapter:
|
|
|
309
296
|
|
|
310
297
|
@pytest.mark.asyncio
|
|
311
298
|
async def test_get_swap_quote_failure(self, adapter, mock_brap_client):
|
|
312
|
-
"""Test swap quote retrieval failure"""
|
|
313
299
|
mock_brap_client.get_quote = AsyncMock(side_effect=Exception("API Error"))
|
|
314
300
|
|
|
315
301
|
success, data = await adapter.get_swap_quote(
|
|
@@ -326,5 +312,4 @@ class TestBRAPAdapter:
|
|
|
326
312
|
assert "API Error" in data
|
|
327
313
|
|
|
328
314
|
def test_adapter_type(self, adapter):
|
|
329
|
-
"""Test adapter has adapter_type"""
|
|
330
315
|
assert adapter.adapter_type == "BRAP"
|
|
@@ -31,21 +31,17 @@ HYPERLEND_DEFAULTS = {
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class HyperlendAdapter(BaseAdapter):
|
|
34
|
-
"""Thin HyperLend adapter that only builds tx data and lets the provider send it."""
|
|
35
|
-
|
|
36
34
|
adapter_type = "HYPERLEND"
|
|
37
35
|
|
|
38
36
|
def __init__(
|
|
39
37
|
self,
|
|
40
38
|
config: dict[str, Any],
|
|
41
|
-
simulation: bool = False,
|
|
42
39
|
strategy_wallet_signing_callback=None,
|
|
43
40
|
) -> None:
|
|
44
41
|
super().__init__("hyperlend_adapter", config)
|
|
45
42
|
cfg = config or {}
|
|
46
43
|
adapter_cfg = cfg.get("hyperlend_adapter") or {}
|
|
47
44
|
|
|
48
|
-
self.simulation = simulation
|
|
49
45
|
self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
|
|
50
46
|
self.hyperlend_client = HyperlendClient()
|
|
51
47
|
|
|
@@ -212,9 +208,6 @@ class HyperlendAdapter(BaseAdapter):
|
|
|
212
208
|
# ------------------------------------------------------------------ #
|
|
213
209
|
|
|
214
210
|
async def _send_tx(self, tx: dict[str, Any]) -> tuple[bool, Any]:
|
|
215
|
-
"""Send transaction with simulation check."""
|
|
216
|
-
if self.simulation:
|
|
217
|
-
return True, {"simulation": tx}
|
|
218
211
|
txn_hash = await send_transaction(tx, self.strategy_wallet_signing_callback)
|
|
219
212
|
return True, txn_hash
|
|
220
213
|
|
|
@@ -250,8 +243,6 @@ class HyperlendAdapter(BaseAdapter):
|
|
|
250
243
|
chain_id: int,
|
|
251
244
|
value: int = 0,
|
|
252
245
|
) -> dict[str, Any]:
|
|
253
|
-
"""Encode calldata without touching network."""
|
|
254
|
-
|
|
255
246
|
async with web3_from_chain_id(chain_id) as web3:
|
|
256
247
|
contract = web3.eth.contract(address=target, abi=abi)
|
|
257
248
|
try:
|
|
@@ -6,17 +6,13 @@ from wayfinder_paths.adapters.hyperlend_adapter.adapter import HyperlendAdapter
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TestHyperlendAdapter:
|
|
9
|
-
"""Test cases for HyperlendAdapter"""
|
|
10
|
-
|
|
11
9
|
@pytest.fixture
|
|
12
10
|
def mock_hyperlend_client(self):
|
|
13
|
-
"""Mock HyperlendClient for testing"""
|
|
14
11
|
mock_client = AsyncMock()
|
|
15
12
|
return mock_client
|
|
16
13
|
|
|
17
14
|
@pytest.fixture
|
|
18
15
|
def adapter(self, mock_hyperlend_client):
|
|
19
|
-
"""Create a HyperlendAdapter instance with mocked client for testing"""
|
|
20
16
|
adapter = HyperlendAdapter(
|
|
21
17
|
config={},
|
|
22
18
|
)
|
|
@@ -25,7 +21,6 @@ class TestHyperlendAdapter:
|
|
|
25
21
|
|
|
26
22
|
@pytest.mark.asyncio
|
|
27
23
|
async def test_get_stable_markets_success(self, adapter, mock_hyperlend_client):
|
|
28
|
-
"""Test successful stable markets retrieval"""
|
|
29
24
|
mock_response = {
|
|
30
25
|
"markets": {
|
|
31
26
|
"0x1234...": {
|
|
@@ -69,7 +64,6 @@ class TestHyperlendAdapter:
|
|
|
69
64
|
async def test_get_stable_markets_minimal_params(
|
|
70
65
|
self, adapter, mock_hyperlend_client
|
|
71
66
|
):
|
|
72
|
-
"""Test stable markets retrieval with only required chain_id"""
|
|
73
67
|
mock_response = {
|
|
74
68
|
"markets": {
|
|
75
69
|
"0x1234...": {
|
|
@@ -100,7 +94,6 @@ class TestHyperlendAdapter:
|
|
|
100
94
|
async def test_get_stable_markets_partial_params(
|
|
101
95
|
self, adapter, mock_hyperlend_client
|
|
102
96
|
):
|
|
103
|
-
"""Test stable markets retrieval with partial optional parameters"""
|
|
104
97
|
mock_response = {"markets": {}, "notes": []}
|
|
105
98
|
mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
|
|
106
99
|
|
|
@@ -118,7 +111,6 @@ class TestHyperlendAdapter:
|
|
|
118
111
|
|
|
119
112
|
@pytest.mark.asyncio
|
|
120
113
|
async def test_get_stable_markets_failure(self, adapter, mock_hyperlend_client):
|
|
121
|
-
"""Test stable markets retrieval failure"""
|
|
122
114
|
mock_hyperlend_client.get_stable_markets = AsyncMock(
|
|
123
115
|
side_effect=Exception("API Error: Connection timeout")
|
|
124
116
|
)
|
|
@@ -130,7 +122,6 @@ class TestHyperlendAdapter:
|
|
|
130
122
|
|
|
131
123
|
@pytest.mark.asyncio
|
|
132
124
|
async def test_get_stable_markets_http_error(self, adapter, mock_hyperlend_client):
|
|
133
|
-
"""Test stable markets retrieval with HTTP error"""
|
|
134
125
|
mock_hyperlend_client.get_stable_markets = AsyncMock(
|
|
135
126
|
side_effect=Exception("HTTP 404 Not Found")
|
|
136
127
|
)
|
|
@@ -144,7 +135,6 @@ class TestHyperlendAdapter:
|
|
|
144
135
|
async def test_get_stable_markets_empty_response(
|
|
145
136
|
self, adapter, mock_hyperlend_client
|
|
146
137
|
):
|
|
147
|
-
"""Test stable markets retrieval with empty response"""
|
|
148
138
|
mock_response = {"markets": {}, "notes": []}
|
|
149
139
|
mock_hyperlend_client.get_stable_markets = AsyncMock(return_value=mock_response)
|
|
150
140
|
|
|
@@ -155,12 +145,10 @@ class TestHyperlendAdapter:
|
|
|
155
145
|
assert len(data.get("markets", {})) == 0
|
|
156
146
|
|
|
157
147
|
def test_adapter_type(self, adapter):
|
|
158
|
-
"""Test adapter has adapter_type"""
|
|
159
148
|
assert adapter.adapter_type == "HYPERLEND"
|
|
160
149
|
|
|
161
150
|
@pytest.mark.asyncio
|
|
162
151
|
async def test_health_check(self, adapter):
|
|
163
|
-
"""Test adapter health check"""
|
|
164
152
|
health = await adapter.health_check()
|
|
165
153
|
assert isinstance(health, dict)
|
|
166
154
|
assert health.get("status") in {"healthy", "unhealthy", "error"}
|
|
@@ -168,7 +156,6 @@ class TestHyperlendAdapter:
|
|
|
168
156
|
|
|
169
157
|
@pytest.mark.asyncio
|
|
170
158
|
async def test_connect(self, adapter):
|
|
171
|
-
"""Test adapter connection"""
|
|
172
159
|
ok = await adapter.connect()
|
|
173
160
|
assert isinstance(ok, bool)
|
|
174
161
|
assert ok is True
|
|
@@ -177,7 +164,6 @@ class TestHyperlendAdapter:
|
|
|
177
164
|
async def test_get_stable_markets_with_is_stable_symbol(
|
|
178
165
|
self, adapter, mock_hyperlend_client
|
|
179
166
|
):
|
|
180
|
-
"""Test stable markets retrieval with is_stable_symbol parameter (ignored by API)"""
|
|
181
167
|
mock_response = {
|
|
182
168
|
"markets": {
|
|
183
169
|
"0x1234...": {
|
|
@@ -206,7 +192,6 @@ class TestHyperlendAdapter:
|
|
|
206
192
|
|
|
207
193
|
@pytest.mark.asyncio
|
|
208
194
|
async def test_get_assets_view_success(self, adapter, mock_hyperlend_client):
|
|
209
|
-
"""Test successful assets view retrieval"""
|
|
210
195
|
mock_response = {
|
|
211
196
|
"block_number": 12345,
|
|
212
197
|
"user": "0x0c737cB5934afCb5B01965141F865F795B324080",
|
|
@@ -271,7 +256,6 @@ class TestHyperlendAdapter:
|
|
|
271
256
|
|
|
272
257
|
@pytest.mark.asyncio
|
|
273
258
|
async def test_get_assets_view_failure(self, adapter, mock_hyperlend_client):
|
|
274
|
-
"""Test assets view retrieval failure"""
|
|
275
259
|
mock_hyperlend_client.get_assets_view = AsyncMock(
|
|
276
260
|
side_effect=Exception("API Error: Invalid address")
|
|
277
261
|
)
|
|
@@ -285,7 +269,6 @@ class TestHyperlendAdapter:
|
|
|
285
269
|
|
|
286
270
|
@pytest.mark.asyncio
|
|
287
271
|
async def test_get_assets_view_empty_response(self, adapter, mock_hyperlend_client):
|
|
288
|
-
"""Test assets view retrieval with empty response"""
|
|
289
272
|
mock_response = {
|
|
290
273
|
"block_number": 12345,
|
|
291
274
|
"user": "0x0c737cB5934afCb5B01965141F865F795B324080",
|