wayfinder-paths 0.1.1__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 (115) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +394 -0
  2. wayfinder_paths/__init__.py +21 -0
  3. wayfinder_paths/config.example.json +20 -0
  4. wayfinder_paths/conftest.py +31 -0
  5. wayfinder_paths/core/__init__.py +13 -0
  6. wayfinder_paths/core/adapters/BaseAdapter.py +48 -0
  7. wayfinder_paths/core/adapters/__init__.py +5 -0
  8. wayfinder_paths/core/adapters/base.py +5 -0
  9. wayfinder_paths/core/clients/AuthClient.py +83 -0
  10. wayfinder_paths/core/clients/BRAPClient.py +90 -0
  11. wayfinder_paths/core/clients/ClientManager.py +231 -0
  12. wayfinder_paths/core/clients/HyperlendClient.py +151 -0
  13. wayfinder_paths/core/clients/LedgerClient.py +222 -0
  14. wayfinder_paths/core/clients/PoolClient.py +96 -0
  15. wayfinder_paths/core/clients/SimulationClient.py +180 -0
  16. wayfinder_paths/core/clients/TokenClient.py +73 -0
  17. wayfinder_paths/core/clients/TransactionClient.py +47 -0
  18. wayfinder_paths/core/clients/WalletClient.py +90 -0
  19. wayfinder_paths/core/clients/WayfinderClient.py +258 -0
  20. wayfinder_paths/core/clients/__init__.py +48 -0
  21. wayfinder_paths/core/clients/protocols.py +295 -0
  22. wayfinder_paths/core/clients/sdk_example.py +115 -0
  23. wayfinder_paths/core/config.py +369 -0
  24. wayfinder_paths/core/constants/__init__.py +26 -0
  25. wayfinder_paths/core/constants/base.py +25 -0
  26. wayfinder_paths/core/constants/erc20_abi.py +118 -0
  27. wayfinder_paths/core/constants/hyperlend_abi.py +152 -0
  28. wayfinder_paths/core/engine/VaultJob.py +182 -0
  29. wayfinder_paths/core/engine/__init__.py +5 -0
  30. wayfinder_paths/core/engine/manifest.py +97 -0
  31. wayfinder_paths/core/services/__init__.py +0 -0
  32. wayfinder_paths/core/services/base.py +177 -0
  33. wayfinder_paths/core/services/local_evm_txn.py +429 -0
  34. wayfinder_paths/core/services/local_token_txn.py +231 -0
  35. wayfinder_paths/core/services/web3_service.py +45 -0
  36. wayfinder_paths/core/settings.py +61 -0
  37. wayfinder_paths/core/strategies/Strategy.py +183 -0
  38. wayfinder_paths/core/strategies/__init__.py +5 -0
  39. wayfinder_paths/core/strategies/base.py +7 -0
  40. wayfinder_paths/core/utils/__init__.py +1 -0
  41. wayfinder_paths/core/utils/evm_helpers.py +165 -0
  42. wayfinder_paths/core/utils/wallets.py +77 -0
  43. wayfinder_paths/core/wallets/README.md +91 -0
  44. wayfinder_paths/core/wallets/WalletManager.py +56 -0
  45. wayfinder_paths/core/wallets/__init__.py +7 -0
  46. wayfinder_paths/run_strategy.py +409 -0
  47. wayfinder_paths/scripts/__init__.py +0 -0
  48. wayfinder_paths/scripts/create_strategy.py +181 -0
  49. wayfinder_paths/scripts/make_wallets.py +160 -0
  50. wayfinder_paths/scripts/validate_manifests.py +213 -0
  51. wayfinder_paths/tests/__init__.py +0 -0
  52. wayfinder_paths/tests/test_smoke_manifest.py +48 -0
  53. wayfinder_paths/tests/test_test_coverage.py +212 -0
  54. wayfinder_paths/tests/test_utils.py +64 -0
  55. wayfinder_paths/vaults/__init__.py +0 -0
  56. wayfinder_paths/vaults/adapters/__init__.py +0 -0
  57. wayfinder_paths/vaults/adapters/balance_adapter/README.md +104 -0
  58. wayfinder_paths/vaults/adapters/balance_adapter/adapter.py +257 -0
  59. wayfinder_paths/vaults/adapters/balance_adapter/examples.json +6 -0
  60. wayfinder_paths/vaults/adapters/balance_adapter/manifest.yaml +8 -0
  61. wayfinder_paths/vaults/adapters/balance_adapter/test_adapter.py +83 -0
  62. wayfinder_paths/vaults/adapters/brap_adapter/README.md +249 -0
  63. wayfinder_paths/vaults/adapters/brap_adapter/__init__.py +7 -0
  64. wayfinder_paths/vaults/adapters/brap_adapter/adapter.py +717 -0
  65. wayfinder_paths/vaults/adapters/brap_adapter/examples.json +175 -0
  66. wayfinder_paths/vaults/adapters/brap_adapter/manifest.yaml +11 -0
  67. wayfinder_paths/vaults/adapters/brap_adapter/test_adapter.py +288 -0
  68. wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +7 -0
  69. wayfinder_paths/vaults/adapters/hyperlend_adapter/adapter.py +298 -0
  70. wayfinder_paths/vaults/adapters/hyperlend_adapter/manifest.yaml +10 -0
  71. wayfinder_paths/vaults/adapters/hyperlend_adapter/test_adapter.py +267 -0
  72. wayfinder_paths/vaults/adapters/ledger_adapter/README.md +158 -0
  73. wayfinder_paths/vaults/adapters/ledger_adapter/__init__.py +7 -0
  74. wayfinder_paths/vaults/adapters/ledger_adapter/adapter.py +286 -0
  75. wayfinder_paths/vaults/adapters/ledger_adapter/examples.json +131 -0
  76. wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +11 -0
  77. wayfinder_paths/vaults/adapters/ledger_adapter/test_adapter.py +202 -0
  78. wayfinder_paths/vaults/adapters/pool_adapter/README.md +218 -0
  79. wayfinder_paths/vaults/adapters/pool_adapter/__init__.py +7 -0
  80. wayfinder_paths/vaults/adapters/pool_adapter/adapter.py +289 -0
  81. wayfinder_paths/vaults/adapters/pool_adapter/examples.json +143 -0
  82. wayfinder_paths/vaults/adapters/pool_adapter/manifest.yaml +10 -0
  83. wayfinder_paths/vaults/adapters/pool_adapter/test_adapter.py +222 -0
  84. wayfinder_paths/vaults/adapters/token_adapter/README.md +101 -0
  85. wayfinder_paths/vaults/adapters/token_adapter/__init__.py +3 -0
  86. wayfinder_paths/vaults/adapters/token_adapter/adapter.py +92 -0
  87. wayfinder_paths/vaults/adapters/token_adapter/examples.json +26 -0
  88. wayfinder_paths/vaults/adapters/token_adapter/manifest.yaml +6 -0
  89. wayfinder_paths/vaults/adapters/token_adapter/test_adapter.py +135 -0
  90. wayfinder_paths/vaults/strategies/__init__.py +0 -0
  91. wayfinder_paths/vaults/strategies/config.py +85 -0
  92. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/README.md +99 -0
  93. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/examples.json +16 -0
  94. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/manifest.yaml +7 -0
  95. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/strategy.py +2328 -0
  96. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/test_strategy.py +319 -0
  97. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/README.md +95 -0
  98. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/examples.json +17 -0
  99. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/manifest.yaml +17 -0
  100. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/strategy.py +1684 -0
  101. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/test_strategy.py +350 -0
  102. wayfinder_paths/vaults/templates/adapter/README.md +105 -0
  103. wayfinder_paths/vaults/templates/adapter/adapter.py +26 -0
  104. wayfinder_paths/vaults/templates/adapter/examples.json +8 -0
  105. wayfinder_paths/vaults/templates/adapter/manifest.yaml +6 -0
  106. wayfinder_paths/vaults/templates/adapter/test_adapter.py +49 -0
  107. wayfinder_paths/vaults/templates/strategy/README.md +152 -0
  108. wayfinder_paths/vaults/templates/strategy/examples.json +11 -0
  109. wayfinder_paths/vaults/templates/strategy/manifest.yaml +8 -0
  110. wayfinder_paths/vaults/templates/strategy/strategy.py +57 -0
  111. wayfinder_paths/vaults/templates/strategy/test_strategy.py +197 -0
  112. wayfinder_paths-0.1.1.dist-info/LICENSE +21 -0
  113. wayfinder_paths-0.1.1.dist-info/METADATA +727 -0
  114. wayfinder_paths-0.1.1.dist-info/RECORD +115 -0
  115. wayfinder_paths-0.1.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,175 @@
1
+ {
2
+ "get_swap_quote": {
3
+ "description": "Get a quote for a cross-chain swap operation",
4
+ "input": {
5
+ "from_token_address": "0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
6
+ "to_token_address": "0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
7
+ "from_chain_id": 8453,
8
+ "to_chain_id": 1,
9
+ "from_address": "0x1234567890123456789012345678901234567890",
10
+ "to_address": "0x1234567890123456789012345678901234567890",
11
+ "amount": "1000000000000000000",
12
+ "slippage": 0.01
13
+ },
14
+ "output": {
15
+ "success": true,
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"
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ }
39
+ },
40
+ "get_best_quote": {
41
+ "description": "Get the best available quote for a swap operation",
42
+ "input": {
43
+ "from_token_address": "0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
44
+ "to_token_address": "0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
45
+ "from_chain_id": 8453,
46
+ "to_chain_id": 1,
47
+ "from_address": "0x1234567890123456789012345678901234567890",
48
+ "to_address": "0x1234567890123456789012345678901234567890",
49
+ "amount": "1000000000000000000",
50
+ "slippage": 0.01
51
+ },
52
+ "output": {
53
+ "success": true,
54
+ "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
64
+ }
65
+ }
66
+ },
67
+ "calculate_swap_fees": {
68
+ "description": "Calculate fees for a swap operation",
69
+ "input": {
70
+ "from_token_address": "0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
71
+ "to_token_address": "0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
72
+ "from_chain_id": 8453,
73
+ "to_chain_id": 1,
74
+ "amount": "1000000000000000000",
75
+ "slippage": 0.01
76
+ },
77
+ "output": {
78
+ "success": true,
79
+ "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
88
+ }
89
+ }
90
+ },
91
+ "compare_routes": {
92
+ "description": "Compare multiple routes for a swap operation",
93
+ "input": {
94
+ "from_token_address": "0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
95
+ "to_token_address": "0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
96
+ "from_chain_id": 8453,
97
+ "to_chain_id": 1,
98
+ "amount": "1000000000000000000",
99
+ "slippage": 0.01
100
+ },
101
+ "output": {
102
+ "success": true,
103
+ "data": {
104
+ "total_routes": 3,
105
+ "best_route": {
106
+ "input_amount": "1000000000000000000",
107
+ "output_amount": "995000000000000000",
108
+ "total_fee": "8000000000000000"
109
+ },
110
+ "all_routes": [
111
+ {
112
+ "route": "Route 1",
113
+ "output_amount": "995000000000000000",
114
+ "total_fee": "8000000000000000",
115
+ "estimated_time": 300
116
+ },
117
+ {
118
+ "route": "Route 2",
119
+ "output_amount": "992000000000000000",
120
+ "total_fee": "12000000000000000",
121
+ "estimated_time": 180
122
+ }
123
+ ],
124
+ "route_analysis": {
125
+ "highest_output": {
126
+ "output_amount": "995000000000000000"
127
+ },
128
+ "lowest_fees": {
129
+ "total_fee": "8000000000000000"
130
+ },
131
+ "fastest": {
132
+ "estimated_time": 180
133
+ }
134
+ }
135
+ }
136
+ }
137
+ },
138
+ "estimate_gas_cost": {
139
+ "description": "Estimate gas costs for a cross-chain operation",
140
+ "input": {
141
+ "from_chain_id": 8453,
142
+ "to_chain_id": 1,
143
+ "operation_type": "swap"
144
+ },
145
+ "output": {
146
+ "success": true,
147
+ "data": {
148
+ "from_chain": "base",
149
+ "to_chain": "ethereum",
150
+ "from_gas_estimate": 100000,
151
+ "to_gas_estimate": 150000,
152
+ "total_operations": 2,
153
+ "operation_type": "swap"
154
+ }
155
+ }
156
+ },
157
+ "validate_swap_parameters": {
158
+ "description": "Validate swap parameters before executing",
159
+ "input": {
160
+ "from_token_address": "0xA0b86a33E6441c8C06DdD4D4c4c4c4c4c4c4c4c4c",
161
+ "to_token_address": "0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
162
+ "from_chain_id": 8453,
163
+ "to_chain_id": 1,
164
+ "amount": "1000000000000000000"
165
+ },
166
+ "output": {
167
+ "success": true,
168
+ "data": {
169
+ "valid": true,
170
+ "quote_available": true,
171
+ "estimated_output": "995000000000000000"
172
+ }
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,11 @@
1
+ schema_version: "0.1"
2
+ entrypoint: "vaults.adapters.brap_adapter.adapter.BRAPAdapter"
3
+ capabilities:
4
+ - "swap.quote"
5
+ - "swap.execute"
6
+ - "bridge.quote"
7
+ - "bridge.execute"
8
+ - "route.optimize"
9
+ - "fee.calculate"
10
+ dependencies:
11
+ - "BRAPClient"
@@ -0,0 +1,288 @@
1
+ from types import SimpleNamespace
2
+ from unittest.mock import AsyncMock, patch
3
+
4
+ import pytest
5
+
6
+ from wayfinder_paths.vaults.adapters.brap_adapter.adapter import BRAPAdapter
7
+
8
+
9
+ class TestBRAPAdapter:
10
+ """Test cases for BRAPAdapter"""
11
+
12
+ @pytest.fixture
13
+ def mock_brap_client(self):
14
+ """Mock BRAPClient for testing"""
15
+ mock_client = AsyncMock()
16
+ return mock_client
17
+
18
+ @pytest.fixture
19
+ def mock_web3_service(self):
20
+ """Minimal Web3Service stub for adapter construction."""
21
+ wallet_provider = SimpleNamespace(
22
+ broadcast_transaction=AsyncMock(return_value=(True, {}))
23
+ )
24
+ token_txn = SimpleNamespace(
25
+ build_send=AsyncMock(return_value=(True, {})),
26
+ build_erc20_approve=AsyncMock(return_value=(True, {})),
27
+ read_erc20_allowance=AsyncMock(return_value={"allowance": 0}),
28
+ )
29
+ return SimpleNamespace(
30
+ evm_transactions=wallet_provider, token_transactions=token_txn
31
+ )
32
+
33
+ @pytest.fixture
34
+ def adapter(self, mock_brap_client, mock_web3_service):
35
+ """Create a BRAPAdapter instance with mocked client for testing"""
36
+ with patch(
37
+ "vaults.adapters.brap_adapter.adapter.BRAPClient",
38
+ return_value=mock_brap_client,
39
+ ):
40
+ return BRAPAdapter(web3_service=mock_web3_service)
41
+
42
+ @pytest.mark.asyncio
43
+ async def test_get_swap_quote_success(self, adapter, mock_brap_client):
44
+ """Test successful swap quote retrieval"""
45
+ mock_response = {
46
+ "quotes": {
47
+ "best_quote": {
48
+ "input_amount": "1000000000000000000",
49
+ "output_amount": "995000000000000000",
50
+ "gas_fee": "5000000000000000",
51
+ "total_fee": "8000000000000000",
52
+ }
53
+ }
54
+ }
55
+ mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
56
+
57
+ success, data = await adapter.get_swap_quote(
58
+ from_token_address="0x" + "a" * 40,
59
+ to_token_address="0x" + "b" * 40,
60
+ from_chain_id=8453,
61
+ to_chain_id=1,
62
+ from_address="0x1234567890123456789012345678901234567890",
63
+ to_address="0x1234567890123456789012345678901234567890",
64
+ amount="1000000000000000000",
65
+ slippage=0.01,
66
+ )
67
+
68
+ assert success is True
69
+ assert data == mock_response
70
+ mock_brap_client.get_quote.assert_called_once_with(
71
+ from_token_address="0x" + "a" * 40,
72
+ to_token_address="0x" + "b" * 40,
73
+ from_chain_id=8453,
74
+ to_chain_id=1,
75
+ from_address="0x1234567890123456789012345678901234567890",
76
+ to_address="0x1234567890123456789012345678901234567890",
77
+ amount1="1000000000000000000",
78
+ slippage=0.01,
79
+ wayfinder_fee=None,
80
+ )
81
+
82
+ @pytest.mark.asyncio
83
+ async def test_get_best_quote_success(self, adapter, mock_brap_client):
84
+ """Test successful best quote retrieval"""
85
+ mock_response = {
86
+ "quotes": {
87
+ "best_quote": {
88
+ "input_amount": "1000000000000000000",
89
+ "output_amount": "995000000000000000",
90
+ "total_fee": "8000000000000000",
91
+ }
92
+ }
93
+ }
94
+ mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
95
+
96
+ success, data = await adapter.get_best_quote(
97
+ from_token_address="0x" + "a" * 40,
98
+ to_token_address="0x" + "b" * 40,
99
+ from_chain_id=8453,
100
+ to_chain_id=1,
101
+ from_address="0x1234567890123456789012345678901234567890",
102
+ to_address="0x1234567890123456789012345678901234567890",
103
+ amount="1000000000000000000",
104
+ )
105
+
106
+ assert success is True
107
+ assert data["input_amount"] == "1000000000000000000"
108
+ assert data["output_amount"] == "995000000000000000"
109
+
110
+ @pytest.mark.asyncio
111
+ async def test_get_best_quote_no_quotes(self, adapter, mock_brap_client):
112
+ """Test best quote retrieval when no quotes available"""
113
+ mock_response = {"quotes": {}}
114
+ mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
115
+
116
+ success, data = await adapter.get_best_quote(
117
+ from_token_address="0x" + "a" * 40,
118
+ to_token_address="0x" + "b" * 40,
119
+ from_chain_id=8453,
120
+ to_chain_id=1,
121
+ from_address="0x1234567890123456789012345678901234567890",
122
+ to_address="0x1234567890123456789012345678901234567890",
123
+ amount="1000000000000000000",
124
+ )
125
+
126
+ assert success is False
127
+ assert "No quotes available" in data
128
+
129
+ @pytest.mark.asyncio
130
+ async def test_calculate_swap_fees_success(self, adapter, mock_brap_client):
131
+ """Test successful swap fee calculation"""
132
+ mock_quote_response = {
133
+ "quotes": {
134
+ "best_quote": {
135
+ "input_amount": "1000000000000000000",
136
+ "output_amount": "995000000000000000",
137
+ "gas_fee": "5000000000000000",
138
+ "bridge_fee": "2000000000000000",
139
+ "protocol_fee": "1000000000000000",
140
+ "total_fee": "8000000000000000",
141
+ "slippage": 0.01,
142
+ "price_impact": 0.005,
143
+ }
144
+ }
145
+ }
146
+ mock_brap_client.get_quote = AsyncMock(return_value=mock_quote_response)
147
+
148
+ success, data = await adapter.calculate_swap_fees(
149
+ from_token_address="0x" + "a" * 40,
150
+ to_token_address="0x" + "b" * 40,
151
+ from_chain_id=8453,
152
+ to_chain_id=1,
153
+ amount="1000000000000000000",
154
+ slippage=0.01,
155
+ )
156
+
157
+ assert success is True
158
+ assert data["input_amount"] == "1000000000000000000"
159
+ assert data["output_amount"] == "995000000000000000"
160
+ assert data["gas_fee"] == "5000000000000000"
161
+ assert data["total_fee"] == "8000000000000000"
162
+
163
+ @pytest.mark.asyncio
164
+ async def test_compare_routes_success(self, adapter, mock_brap_client):
165
+ """Test successful route comparison"""
166
+ mock_response = {
167
+ "quotes": {
168
+ "quotes": [
169
+ {
170
+ "route": "Route 1",
171
+ "output_amount": "995000000000000000",
172
+ "total_fee": "8000000000000000",
173
+ "estimated_time": 300,
174
+ },
175
+ {
176
+ "route": "Route 2",
177
+ "output_amount": "992000000000000000",
178
+ "total_fee": "12000000000000000",
179
+ "estimated_time": 180,
180
+ },
181
+ ],
182
+ "best_quote": {
183
+ "output_amount": "995000000000000000",
184
+ "total_fee": "8000000000000000",
185
+ },
186
+ }
187
+ }
188
+ mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
189
+
190
+ success, data = await adapter.compare_routes(
191
+ from_token_address="0x" + "a" * 40,
192
+ to_token_address="0x" + "b" * 40,
193
+ from_chain_id=8453,
194
+ to_chain_id=1,
195
+ amount="1000000000000000000",
196
+ )
197
+
198
+ assert success is True
199
+ assert data["total_routes"] == 2
200
+ assert len(data["all_routes"]) == 2
201
+ assert data["best_route"]["output_amount"] == "995000000000000000"
202
+
203
+ @pytest.mark.asyncio
204
+ async def test_estimate_gas_cost_success(self, adapter):
205
+ """Test successful gas cost estimation"""
206
+ success, data = await adapter.estimate_gas_cost(
207
+ from_chain_id=8453, to_chain_id=1, operation_type="swap"
208
+ )
209
+
210
+ assert success is True
211
+ assert data["from_chain"] == "base"
212
+ assert data["to_chain"] == "ethereum"
213
+ assert data["from_gas_estimate"] == 100000
214
+ assert data["to_gas_estimate"] == 150000
215
+ assert data["total_operations"] == 2
216
+
217
+ @pytest.mark.asyncio
218
+ async def test_validate_swap_parameters_success(self, adapter, mock_brap_client):
219
+ """Test successful swap parameter validation"""
220
+ mock_quote_response = {
221
+ "quotes": {"best_quote": {"output_amount": "995000000000000000"}}
222
+ }
223
+ mock_brap_client.get_quote = AsyncMock(return_value=mock_quote_response)
224
+
225
+ success, data = await adapter.validate_swap_parameters(
226
+ from_token_address="0x" + "a" * 40,
227
+ to_token_address="0x" + "b" * 40,
228
+ from_chain_id=8453,
229
+ to_chain_id=1,
230
+ amount="1000000000000000000",
231
+ )
232
+
233
+ assert success is True
234
+ assert data["valid"] is True
235
+ assert data["quote_available"] is True
236
+ assert data["estimated_output"] == "995000000000000000"
237
+
238
+ @pytest.mark.asyncio
239
+ async def test_validate_swap_parameters_invalid_address(self, adapter):
240
+ """Test swap parameter validation with invalid address"""
241
+ success, data = await adapter.validate_swap_parameters(
242
+ from_token_address="invalid_address",
243
+ to_token_address="0xB1c97a44F7552d9Dd5e5e5e5e5e5e5e5e5e5e5e5e5e",
244
+ from_chain_id=8453,
245
+ to_chain_id=1,
246
+ amount="1000000000000000000",
247
+ )
248
+
249
+ assert success is False
250
+ assert data["valid"] is False
251
+ assert "Invalid from_token_address" in data["errors"]
252
+
253
+ @pytest.mark.asyncio
254
+ async def test_validate_swap_parameters_invalid_amount(self, adapter):
255
+ """Test swap parameter validation with invalid amount"""
256
+ success, data = await adapter.validate_swap_parameters(
257
+ from_token_address="0x" + "a" * 40,
258
+ to_token_address="0x" + "b" * 40,
259
+ from_chain_id=8453,
260
+ to_chain_id=1,
261
+ amount="invalid_amount",
262
+ )
263
+
264
+ assert success is False
265
+ assert data["valid"] is False
266
+ assert "Invalid amount format" in data["errors"]
267
+
268
+ @pytest.mark.asyncio
269
+ async def test_get_swap_quote_failure(self, adapter, mock_brap_client):
270
+ """Test swap quote retrieval failure"""
271
+ mock_brap_client.get_quote = AsyncMock(side_effect=Exception("API Error"))
272
+
273
+ success, data = await adapter.get_swap_quote(
274
+ from_token_address="0x" + "a" * 40,
275
+ to_token_address="0x" + "b" * 40,
276
+ from_chain_id=8453,
277
+ to_chain_id=1,
278
+ from_address="0x1234567890123456789012345678901234567890",
279
+ to_address="0x1234567890123456789012345678901234567890",
280
+ amount="1000000000000000000",
281
+ )
282
+
283
+ assert success is False
284
+ assert "API Error" in data
285
+
286
+ def test_adapter_type(self, adapter):
287
+ """Test adapter has adapter_type"""
288
+ assert adapter.adapter_type == "BRAP"
@@ -0,0 +1,7 @@
1
+ """
2
+ Hyperlend Adapter
3
+ """
4
+
5
+ from wayfinder_paths.vaults.adapters.hyperlend_adapter.adapter import HyperlendAdapter
6
+
7
+ __all__ = ["HyperlendAdapter"]