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.
- wayfinder_paths/CONFIG_GUIDE.md +399 -0
- wayfinder_paths/__init__.py +22 -0
- wayfinder_paths/abis/generic/erc20.json +383 -0
- wayfinder_paths/adapters/__init__.py +0 -0
- wayfinder_paths/adapters/balance_adapter/README.md +94 -0
- wayfinder_paths/adapters/balance_adapter/adapter.py +238 -0
- wayfinder_paths/adapters/balance_adapter/examples.json +6 -0
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +59 -0
- wayfinder_paths/adapters/brap_adapter/README.md +249 -0
- wayfinder_paths/adapters/brap_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/brap_adapter/adapter.py +726 -0
- wayfinder_paths/adapters/brap_adapter/examples.json +175 -0
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +11 -0
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +286 -0
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +305 -0
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +10 -0
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +274 -0
- wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +18 -0
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +1093 -0
- wayfinder_paths/adapters/hyperliquid_adapter/executor.py +549 -0
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +8 -0
- wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +1050 -0
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +126 -0
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +219 -0
- wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +220 -0
- wayfinder_paths/adapters/hyperliquid_adapter/utils.py +134 -0
- wayfinder_paths/adapters/ledger_adapter/README.md +145 -0
- wayfinder_paths/adapters/ledger_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/ledger_adapter/adapter.py +289 -0
- wayfinder_paths/adapters/ledger_adapter/examples.json +137 -0
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +11 -0
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +205 -0
- wayfinder_paths/adapters/pool_adapter/README.md +206 -0
- wayfinder_paths/adapters/pool_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/pool_adapter/adapter.py +282 -0
- wayfinder_paths/adapters/pool_adapter/examples.json +143 -0
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +10 -0
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +220 -0
- wayfinder_paths/adapters/token_adapter/README.md +101 -0
- wayfinder_paths/adapters/token_adapter/__init__.py +3 -0
- wayfinder_paths/adapters/token_adapter/adapter.py +96 -0
- wayfinder_paths/adapters/token_adapter/examples.json +26 -0
- wayfinder_paths/adapters/token_adapter/manifest.yaml +6 -0
- wayfinder_paths/adapters/token_adapter/test_adapter.py +125 -0
- wayfinder_paths/config.example.json +22 -0
- wayfinder_paths/conftest.py +31 -0
- wayfinder_paths/core/__init__.py +18 -0
- wayfinder_paths/core/adapters/BaseAdapter.py +65 -0
- wayfinder_paths/core/adapters/__init__.py +5 -0
- wayfinder_paths/core/adapters/base.py +5 -0
- wayfinder_paths/core/adapters/models.py +46 -0
- wayfinder_paths/core/analytics/__init__.py +11 -0
- wayfinder_paths/core/analytics/bootstrap.py +57 -0
- wayfinder_paths/core/analytics/stats.py +48 -0
- wayfinder_paths/core/analytics/test_analytics.py +170 -0
- wayfinder_paths/core/clients/AuthClient.py +83 -0
- wayfinder_paths/core/clients/BRAPClient.py +109 -0
- wayfinder_paths/core/clients/ClientManager.py +210 -0
- wayfinder_paths/core/clients/HyperlendClient.py +192 -0
- wayfinder_paths/core/clients/LedgerClient.py +443 -0
- wayfinder_paths/core/clients/PoolClient.py +128 -0
- wayfinder_paths/core/clients/SimulationClient.py +192 -0
- wayfinder_paths/core/clients/TokenClient.py +89 -0
- wayfinder_paths/core/clients/TransactionClient.py +63 -0
- wayfinder_paths/core/clients/WalletClient.py +94 -0
- wayfinder_paths/core/clients/WayfinderClient.py +269 -0
- wayfinder_paths/core/clients/__init__.py +48 -0
- wayfinder_paths/core/clients/protocols.py +392 -0
- wayfinder_paths/core/clients/sdk_example.py +110 -0
- wayfinder_paths/core/config.py +458 -0
- wayfinder_paths/core/constants/__init__.py +26 -0
- wayfinder_paths/core/constants/base.py +42 -0
- wayfinder_paths/core/constants/erc20_abi.py +118 -0
- wayfinder_paths/core/constants/hyperlend_abi.py +152 -0
- wayfinder_paths/core/engine/StrategyJob.py +188 -0
- wayfinder_paths/core/engine/__init__.py +5 -0
- wayfinder_paths/core/engine/manifest.py +97 -0
- wayfinder_paths/core/services/__init__.py +0 -0
- wayfinder_paths/core/services/base.py +179 -0
- wayfinder_paths/core/services/local_evm_txn.py +430 -0
- wayfinder_paths/core/services/local_token_txn.py +231 -0
- wayfinder_paths/core/services/web3_service.py +45 -0
- wayfinder_paths/core/settings.py +61 -0
- wayfinder_paths/core/strategies/Strategy.py +280 -0
- wayfinder_paths/core/strategies/__init__.py +5 -0
- wayfinder_paths/core/strategies/base.py +7 -0
- wayfinder_paths/core/strategies/descriptors.py +81 -0
- wayfinder_paths/core/utils/__init__.py +1 -0
- wayfinder_paths/core/utils/evm_helpers.py +206 -0
- wayfinder_paths/core/utils/wallets.py +77 -0
- wayfinder_paths/core/wallets/README.md +91 -0
- wayfinder_paths/core/wallets/WalletManager.py +56 -0
- wayfinder_paths/core/wallets/__init__.py +7 -0
- wayfinder_paths/policies/enso.py +17 -0
- wayfinder_paths/policies/erc20.py +34 -0
- wayfinder_paths/policies/evm.py +21 -0
- wayfinder_paths/policies/hyper_evm.py +19 -0
- wayfinder_paths/policies/hyperlend.py +12 -0
- wayfinder_paths/policies/hyperliquid.py +30 -0
- wayfinder_paths/policies/moonwell.py +54 -0
- wayfinder_paths/policies/prjx.py +30 -0
- wayfinder_paths/policies/util.py +27 -0
- wayfinder_paths/run_strategy.py +411 -0
- wayfinder_paths/scripts/__init__.py +0 -0
- wayfinder_paths/scripts/create_strategy.py +181 -0
- wayfinder_paths/scripts/make_wallets.py +169 -0
- wayfinder_paths/scripts/run_strategy.py +124 -0
- wayfinder_paths/scripts/validate_manifests.py +213 -0
- wayfinder_paths/strategies/__init__.py +0 -0
- wayfinder_paths/strategies/basis_trading_strategy/README.md +213 -0
- wayfinder_paths/strategies/basis_trading_strategy/__init__.py +3 -0
- wayfinder_paths/strategies/basis_trading_strategy/constants.py +1 -0
- wayfinder_paths/strategies/basis_trading_strategy/examples.json +16 -0
- wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +23 -0
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1011 -0
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +4522 -0
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +727 -0
- wayfinder_paths/strategies/basis_trading_strategy/types.py +39 -0
- wayfinder_paths/strategies/config.py +85 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +100 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +8 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +7 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +2270 -0
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +352 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +96 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/examples.json +17 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +17 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +1810 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +520 -0
- wayfinder_paths/templates/adapter/README.md +105 -0
- wayfinder_paths/templates/adapter/adapter.py +26 -0
- wayfinder_paths/templates/adapter/examples.json +8 -0
- wayfinder_paths/templates/adapter/manifest.yaml +6 -0
- wayfinder_paths/templates/adapter/test_adapter.py +49 -0
- wayfinder_paths/templates/strategy/README.md +153 -0
- wayfinder_paths/templates/strategy/examples.json +11 -0
- wayfinder_paths/templates/strategy/manifest.yaml +8 -0
- wayfinder_paths/templates/strategy/strategy.py +57 -0
- wayfinder_paths/templates/strategy/test_strategy.py +197 -0
- wayfinder_paths/tests/__init__.py +0 -0
- wayfinder_paths/tests/test_smoke_manifest.py +48 -0
- wayfinder_paths/tests/test_test_coverage.py +212 -0
- wayfinder_paths/tests/test_utils.py +64 -0
- wayfinder_paths-0.1.7.dist-info/LICENSE +21 -0
- wayfinder_paths-0.1.7.dist-info/METADATA +777 -0
- wayfinder_paths-0.1.7.dist-info/RECORD +149 -0
- 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,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"
|