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