wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.15__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/adapters/balance_adapter/README.md +13 -14
- wayfinder_paths/adapters/balance_adapter/adapter.py +73 -32
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
- wayfinder_paths/adapters/brap_adapter/README.md +11 -16
- wayfinder_paths/adapters/brap_adapter/adapter.py +144 -78
- wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +127 -65
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +30 -14
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +121 -67
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +6 -6
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +12 -12
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +6 -6
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +332 -9
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +13 -13
- wayfinder_paths/adapters/pool_adapter/README.md +9 -10
- wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +2 -2
- wayfinder_paths/adapters/token_adapter/README.md +2 -14
- wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
- wayfinder_paths/adapters/token_adapter/examples.json +4 -8
- wayfinder_paths/adapters/token_adapter/test_adapter.py +9 -7
- wayfinder_paths/core/clients/BRAPClient.py +102 -61
- wayfinder_paths/core/clients/ClientManager.py +1 -68
- wayfinder_paths/core/clients/HyperlendClient.py +125 -64
- wayfinder_paths/core/clients/LedgerClient.py +1 -4
- wayfinder_paths/core/clients/PoolClient.py +122 -48
- wayfinder_paths/core/clients/TokenClient.py +91 -36
- wayfinder_paths/core/clients/WalletClient.py +26 -56
- wayfinder_paths/core/clients/WayfinderClient.py +28 -160
- wayfinder_paths/core/clients/__init__.py +0 -2
- wayfinder_paths/core/clients/protocols.py +35 -46
- wayfinder_paths/core/clients/sdk_example.py +37 -22
- wayfinder_paths/core/constants/erc20_abi.py +0 -11
- wayfinder_paths/core/engine/StrategyJob.py +10 -56
- wayfinder_paths/core/services/base.py +1 -0
- wayfinder_paths/core/services/local_evm_txn.py +25 -9
- wayfinder_paths/core/services/local_token_txn.py +2 -6
- wayfinder_paths/core/services/test_local_evm_txn.py +145 -0
- wayfinder_paths/core/strategies/Strategy.py +16 -4
- wayfinder_paths/core/utils/evm_helpers.py +2 -9
- wayfinder_paths/policies/erc20.py +1 -1
- wayfinder_paths/run_strategy.py +13 -19
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +77 -11
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +6 -6
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +107 -23
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +6 -5
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2246 -1279
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +276 -109
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +153 -56
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
- wayfinder_paths/templates/adapter/README.md +1 -1
- wayfinder_paths/templates/strategy/README.md +3 -3
- wayfinder_paths/templates/strategy/test_strategy.py +3 -2
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/METADATA +14 -49
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/RECORD +59 -60
- wayfinder_paths/abis/generic/erc20.json +0 -383
- wayfinder_paths/core/clients/AuthClient.py +0 -83
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.15.dist-info}/WHEEL +0 -0
|
@@ -41,14 +41,34 @@ class TestBRAPAdapter:
|
|
|
41
41
|
async def test_get_swap_quote_success(self, adapter, mock_brap_client):
|
|
42
42
|
"""Test successful swap quote retrieval"""
|
|
43
43
|
mock_response = {
|
|
44
|
-
"quotes":
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
44
|
+
"quotes": [
|
|
45
|
+
{
|
|
46
|
+
"provider": "enso",
|
|
47
|
+
"input_amount": 1000000000000000000,
|
|
48
|
+
"output_amount": 995000000000000000,
|
|
49
|
+
"calldata": {
|
|
50
|
+
"data": "0x",
|
|
51
|
+
"to": "0x",
|
|
52
|
+
"from_address": "0x",
|
|
53
|
+
"value": "0",
|
|
54
|
+
"chainId": 8453,
|
|
55
|
+
},
|
|
56
|
+
"fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
|
|
50
57
|
}
|
|
51
|
-
|
|
58
|
+
],
|
|
59
|
+
"best_quote": {
|
|
60
|
+
"provider": "enso",
|
|
61
|
+
"input_amount": 1000000000000000000,
|
|
62
|
+
"output_amount": 995000000000000000,
|
|
63
|
+
"calldata": {
|
|
64
|
+
"data": "0x",
|
|
65
|
+
"to": "0x",
|
|
66
|
+
"from_address": "0x",
|
|
67
|
+
"value": "0",
|
|
68
|
+
"chainId": 8453,
|
|
69
|
+
},
|
|
70
|
+
"fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
|
|
71
|
+
},
|
|
52
72
|
}
|
|
53
73
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
|
|
54
74
|
|
|
@@ -63,29 +83,35 @@ class TestBRAPAdapter:
|
|
|
63
83
|
slippage=0.01,
|
|
64
84
|
)
|
|
65
85
|
|
|
66
|
-
assert success
|
|
86
|
+
assert success
|
|
67
87
|
assert data == mock_response
|
|
68
88
|
mock_brap_client.get_quote.assert_called_once_with(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
amount1="1000000000000000000",
|
|
76
|
-
slippage=0.01,
|
|
77
|
-
wayfinder_fee=None,
|
|
89
|
+
from_token="0x" + "a" * 40,
|
|
90
|
+
to_token="0x" + "b" * 40,
|
|
91
|
+
from_chain=8453,
|
|
92
|
+
to_chain=1,
|
|
93
|
+
from_wallet="0x1234567890123456789012345678901234567890",
|
|
94
|
+
from_amount="1000000000000000000",
|
|
78
95
|
)
|
|
79
96
|
|
|
80
97
|
@pytest.mark.asyncio
|
|
81
98
|
async def test_get_best_quote_success(self, adapter, mock_brap_client):
|
|
82
99
|
"""Test successful best quote retrieval"""
|
|
83
100
|
mock_response = {
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
|
|
101
|
+
"quotes": [],
|
|
102
|
+
"best_quote": {
|
|
103
|
+
"provider": "enso",
|
|
104
|
+
"input_amount": 1000000000000000000,
|
|
105
|
+
"output_amount": 995000000000000000,
|
|
106
|
+
"calldata": {
|
|
107
|
+
"data": "0x",
|
|
108
|
+
"to": "0x",
|
|
109
|
+
"from_address": "0x",
|
|
110
|
+
"value": "0",
|
|
111
|
+
"chainId": 8453,
|
|
112
|
+
},
|
|
113
|
+
"fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
|
|
114
|
+
},
|
|
89
115
|
}
|
|
90
116
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
|
|
91
117
|
|
|
@@ -99,14 +125,14 @@ class TestBRAPAdapter:
|
|
|
99
125
|
amount="1000000000000000000",
|
|
100
126
|
)
|
|
101
127
|
|
|
102
|
-
assert success
|
|
103
|
-
assert data["input_amount"] ==
|
|
104
|
-
assert data["output_amount"] ==
|
|
128
|
+
assert success
|
|
129
|
+
assert data["input_amount"] == 1000000000000000000
|
|
130
|
+
assert data["output_amount"] == 995000000000000000
|
|
105
131
|
|
|
106
132
|
@pytest.mark.asyncio
|
|
107
133
|
async def test_get_best_quote_no_quotes(self, adapter, mock_brap_client):
|
|
108
134
|
"""Test best quote retrieval when no quotes available"""
|
|
109
|
-
mock_response = {"
|
|
135
|
+
mock_response = {"quotes": [], "best_quote": None}
|
|
110
136
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
|
|
111
137
|
|
|
112
138
|
success, data = await adapter.get_best_quote(
|
|
@@ -126,18 +152,25 @@ class TestBRAPAdapter:
|
|
|
126
152
|
async def test_calculate_swap_fees_success(self, adapter, mock_brap_client):
|
|
127
153
|
"""Test successful swap fee calculation"""
|
|
128
154
|
mock_quote_response = {
|
|
129
|
-
"quotes":
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
}
|
|
140
|
-
|
|
155
|
+
"quotes": [],
|
|
156
|
+
"best_quote": {
|
|
157
|
+
"provider": "enso",
|
|
158
|
+
"input_amount": 1000000000000000000,
|
|
159
|
+
"output_amount": 995000000000000000,
|
|
160
|
+
"gas_estimate": 5000000000000000,
|
|
161
|
+
"quote": {"priceImpact": 5},
|
|
162
|
+
"fee_estimate": {
|
|
163
|
+
"fee_total_usd": 0.008,
|
|
164
|
+
"fee_breakdown": [],
|
|
165
|
+
},
|
|
166
|
+
"calldata": {
|
|
167
|
+
"data": "0x",
|
|
168
|
+
"to": "0x",
|
|
169
|
+
"from_address": "0x",
|
|
170
|
+
"value": "0",
|
|
171
|
+
"chainId": 8453,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
141
174
|
}
|
|
142
175
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_quote_response)
|
|
143
176
|
|
|
@@ -150,36 +183,54 @@ class TestBRAPAdapter:
|
|
|
150
183
|
slippage=0.01,
|
|
151
184
|
)
|
|
152
185
|
|
|
153
|
-
assert success
|
|
154
|
-
assert data["input_amount"] ==
|
|
155
|
-
assert data["output_amount"] ==
|
|
156
|
-
assert data["gas_fee"] ==
|
|
157
|
-
assert data["total_fee"] ==
|
|
186
|
+
assert success
|
|
187
|
+
assert data["input_amount"] == 1000000000000000000
|
|
188
|
+
assert data["output_amount"] == 995000000000000000
|
|
189
|
+
assert data["gas_fee"] == 5000000000000000
|
|
190
|
+
assert data["total_fee"] == 0.008
|
|
158
191
|
|
|
159
192
|
@pytest.mark.asyncio
|
|
160
193
|
async def test_compare_routes_success(self, adapter, mock_brap_client):
|
|
161
194
|
"""Test successful route comparison"""
|
|
162
195
|
mock_response = {
|
|
163
|
-
"quotes":
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"
|
|
196
|
+
"quotes": [
|
|
197
|
+
{
|
|
198
|
+
"provider": "enso",
|
|
199
|
+
"output_amount": 995000000000000000,
|
|
200
|
+
"fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
|
|
201
|
+
"calldata": {
|
|
202
|
+
"data": "0x",
|
|
203
|
+
"to": "0x",
|
|
204
|
+
"from_address": "0x",
|
|
205
|
+
"value": "0",
|
|
206
|
+
"chainId": 8453,
|
|
170
207
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"provider": "enso",
|
|
211
|
+
"output_amount": 992000000000000000,
|
|
212
|
+
"fee_estimate": {"fee_total_usd": 0.012, "fee_breakdown": []},
|
|
213
|
+
"calldata": {
|
|
214
|
+
"data": "0x",
|
|
215
|
+
"to": "0x",
|
|
216
|
+
"from_address": "0x",
|
|
217
|
+
"value": "0",
|
|
218
|
+
"chainId": 8453,
|
|
176
219
|
},
|
|
177
|
-
],
|
|
178
|
-
"best_quote": {
|
|
179
|
-
"output_amount": "995000000000000000",
|
|
180
|
-
"total_fee": "8000000000000000",
|
|
181
220
|
},
|
|
182
|
-
|
|
221
|
+
],
|
|
222
|
+
"best_quote": {
|
|
223
|
+
"provider": "enso",
|
|
224
|
+
"output_amount": 995000000000000000,
|
|
225
|
+
"fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
|
|
226
|
+
"calldata": {
|
|
227
|
+
"data": "0x",
|
|
228
|
+
"to": "0x",
|
|
229
|
+
"from_address": "0x",
|
|
230
|
+
"value": "0",
|
|
231
|
+
"chainId": 8453,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
183
234
|
}
|
|
184
235
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_response)
|
|
185
236
|
|
|
@@ -191,10 +242,10 @@ class TestBRAPAdapter:
|
|
|
191
242
|
amount="1000000000000000000",
|
|
192
243
|
)
|
|
193
244
|
|
|
194
|
-
assert success
|
|
245
|
+
assert success
|
|
195
246
|
assert data["total_routes"] == 2
|
|
196
247
|
assert len(data["all_routes"]) == 2
|
|
197
|
-
assert data["best_route"]["output_amount"] ==
|
|
248
|
+
assert data["best_route"]["output_amount"] == 995000000000000000
|
|
198
249
|
|
|
199
250
|
@pytest.mark.asyncio
|
|
200
251
|
async def test_estimate_gas_cost_success(self, adapter):
|
|
@@ -203,7 +254,7 @@ class TestBRAPAdapter:
|
|
|
203
254
|
from_chain_id=8453, to_chain_id=1, operation_type="swap"
|
|
204
255
|
)
|
|
205
256
|
|
|
206
|
-
assert success
|
|
257
|
+
assert success
|
|
207
258
|
assert data["from_chain"] == "base"
|
|
208
259
|
assert data["to_chain"] == "ethereum"
|
|
209
260
|
assert data["from_gas_estimate"] == 100000
|
|
@@ -214,7 +265,18 @@ class TestBRAPAdapter:
|
|
|
214
265
|
async def test_validate_swap_parameters_success(self, adapter, mock_brap_client):
|
|
215
266
|
"""Test successful swap parameter validation"""
|
|
216
267
|
mock_quote_response = {
|
|
217
|
-
"quotes":
|
|
268
|
+
"quotes": [],
|
|
269
|
+
"best_quote": {
|
|
270
|
+
"output_amount": 995000000000000000,
|
|
271
|
+
"calldata": {
|
|
272
|
+
"data": "0x",
|
|
273
|
+
"to": "0x",
|
|
274
|
+
"from_address": "0x",
|
|
275
|
+
"value": "0",
|
|
276
|
+
"chainId": 8453,
|
|
277
|
+
},
|
|
278
|
+
"fee_estimate": {"fee_total_usd": 0.008, "fee_breakdown": []},
|
|
279
|
+
},
|
|
218
280
|
}
|
|
219
281
|
mock_brap_client.get_quote = AsyncMock(return_value=mock_quote_response)
|
|
220
282
|
|
|
@@ -226,7 +288,7 @@ class TestBRAPAdapter:
|
|
|
226
288
|
amount="1000000000000000000",
|
|
227
289
|
)
|
|
228
290
|
|
|
229
|
-
assert success
|
|
291
|
+
assert success
|
|
230
292
|
assert data["valid"] is True
|
|
231
293
|
assert data["quote_available"] is True
|
|
232
294
|
assert data["estimated_output"] == "995000000000000000"
|
|
@@ -10,7 +10,7 @@ from wayfinder_paths.core.clients.HyperlendClient import (
|
|
|
10
10
|
HyperlendClient,
|
|
11
11
|
LendRateHistory,
|
|
12
12
|
MarketEntry,
|
|
13
|
-
|
|
13
|
+
StableMarketsHeadroomResponse,
|
|
14
14
|
)
|
|
15
15
|
from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
|
|
16
16
|
from wayfinder_paths.core.constants.hyperlend_abi import (
|
|
@@ -66,55 +66,71 @@ class HyperlendAdapter(BaseAdapter):
|
|
|
66
66
|
|
|
67
67
|
async def get_stable_markets(
|
|
68
68
|
self,
|
|
69
|
-
|
|
69
|
+
*,
|
|
70
70
|
required_underlying_tokens: float | None = None,
|
|
71
71
|
buffer_bps: int | None = None,
|
|
72
72
|
min_buffer_tokens: float | None = None,
|
|
73
|
-
|
|
74
|
-
) -> tuple[bool, list[StableMarket] | str]:
|
|
73
|
+
) -> tuple[bool, StableMarketsHeadroomResponse | str]:
|
|
75
74
|
try:
|
|
76
75
|
data = await self.hyperlend_client.get_stable_markets(
|
|
77
|
-
chain_id=chain_id,
|
|
78
76
|
required_underlying_tokens=required_underlying_tokens,
|
|
79
77
|
buffer_bps=buffer_bps,
|
|
80
78
|
min_buffer_tokens=min_buffer_tokens,
|
|
81
|
-
is_stable_symbol=is_stable_symbol,
|
|
82
79
|
)
|
|
80
|
+
# Strategies expect a dict with "markets" and "notes"; normalize if API returns a list
|
|
81
|
+
if isinstance(data, list):
|
|
82
|
+
markets: dict[str, Any] = {}
|
|
83
|
+
for i, item in enumerate(data):
|
|
84
|
+
if isinstance(item, dict) and "address" in item:
|
|
85
|
+
markets[item["address"]] = item
|
|
86
|
+
elif isinstance(item, dict):
|
|
87
|
+
markets[str(i)] = item
|
|
88
|
+
data = {"markets": markets, "notes": []}
|
|
89
|
+
elif isinstance(data, dict) and ("markets" not in data or "notes" not in data):
|
|
90
|
+
data = {
|
|
91
|
+
"markets": data.get("markets", {}),
|
|
92
|
+
"notes": data.get("notes", []),
|
|
93
|
+
}
|
|
83
94
|
return True, data
|
|
84
95
|
except Exception as exc:
|
|
85
96
|
return False, str(exc)
|
|
86
97
|
|
|
87
98
|
async def get_assets_view(
|
|
88
|
-
self,
|
|
99
|
+
self,
|
|
100
|
+
*,
|
|
101
|
+
user_address: str,
|
|
89
102
|
) -> tuple[bool, AssetsView | str]:
|
|
90
103
|
try:
|
|
91
104
|
data = await self.hyperlend_client.get_assets_view(
|
|
92
|
-
|
|
105
|
+
user_address=user_address
|
|
93
106
|
)
|
|
94
107
|
return True, data
|
|
95
108
|
except Exception as exc:
|
|
96
109
|
return False, str(exc)
|
|
97
110
|
|
|
98
111
|
async def get_market_entry(
|
|
99
|
-
self,
|
|
112
|
+
self,
|
|
113
|
+
*,
|
|
114
|
+
token: str,
|
|
100
115
|
) -> tuple[bool, MarketEntry | str]:
|
|
101
116
|
try:
|
|
102
|
-
data = await self.hyperlend_client.get_market_entry(
|
|
117
|
+
data = await self.hyperlend_client.get_market_entry(token=token)
|
|
103
118
|
return True, data
|
|
104
119
|
except Exception as exc:
|
|
105
120
|
return False, str(exc)
|
|
106
121
|
|
|
107
122
|
async def get_lend_rate_history(
|
|
108
123
|
self,
|
|
109
|
-
|
|
110
|
-
|
|
124
|
+
*,
|
|
125
|
+
token: str,
|
|
111
126
|
lookback_hours: int,
|
|
127
|
+
force_refresh: bool | None = None,
|
|
112
128
|
) -> tuple[bool, LendRateHistory | str]:
|
|
113
129
|
try:
|
|
114
130
|
data = await self.hyperlend_client.get_lend_rate_history(
|
|
115
|
-
|
|
116
|
-
token_address=token_address,
|
|
131
|
+
token=token,
|
|
117
132
|
lookback_hours=lookback_hours,
|
|
133
|
+
force_refresh=force_refresh,
|
|
118
134
|
)
|
|
119
135
|
return True, data
|
|
120
136
|
except Exception as exc:
|