wayfinder-paths 0.1.23__py3-none-any.whl → 0.1.24__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 (122) hide show
  1. wayfinder_paths/adapters/balance_adapter/adapter.py +250 -0
  2. wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +0 -11
  4. wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
  5. wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
  6. wayfinder_paths/adapters/boros_adapter/client.py +476 -0
  7. wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
  8. wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
  9. wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
  10. wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
  11. wayfinder_paths/adapters/boros_adapter/types.py +70 -0
  12. wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
  13. wayfinder_paths/adapters/brap_adapter/adapter.py +1 -1
  14. wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
  15. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +161 -26
  16. wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
  17. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +77 -13
  18. wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
  19. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +585 -61
  20. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
  21. wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
  22. wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
  23. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
  24. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
  25. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
  26. wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
  27. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
  28. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
  29. wayfinder_paths/adapters/moonwell_adapter/adapter.py +592 -400
  30. wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
  31. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +126 -219
  32. wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
  33. wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
  34. wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
  35. wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
  36. wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
  37. wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
  38. wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
  39. wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
  40. wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
  41. wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
  42. wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
  43. wayfinder_paths/adapters/token_adapter/examples.json +0 -4
  44. wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
  45. wayfinder_paths/conftest.py +24 -17
  46. wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
  47. wayfinder_paths/core/adapters/models.py +17 -7
  48. wayfinder_paths/core/clients/BRAPClient.py +1 -1
  49. wayfinder_paths/core/clients/TokenClient.py +47 -1
  50. wayfinder_paths/core/clients/WayfinderClient.py +1 -2
  51. wayfinder_paths/core/clients/protocols.py +21 -22
  52. wayfinder_paths/core/clients/test_ledger_client.py +448 -0
  53. wayfinder_paths/core/config.py +12 -0
  54. wayfinder_paths/core/constants/__init__.py +15 -0
  55. wayfinder_paths/core/constants/base.py +6 -1
  56. wayfinder_paths/core/constants/contracts.py +39 -26
  57. wayfinder_paths/core/constants/erc20_abi.py +0 -1
  58. wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
  59. wayfinder_paths/core/constants/hyperliquid.py +16 -0
  60. wayfinder_paths/core/constants/moonwell_abi.py +0 -15
  61. wayfinder_paths/core/engine/manifest.py +66 -0
  62. wayfinder_paths/core/strategies/Strategy.py +0 -61
  63. wayfinder_paths/core/strategies/__init__.py +10 -1
  64. wayfinder_paths/core/strategies/opa_loop.py +167 -0
  65. wayfinder_paths/core/utils/test_transaction.py +289 -0
  66. wayfinder_paths/core/utils/transaction.py +44 -1
  67. wayfinder_paths/core/utils/web3.py +3 -0
  68. wayfinder_paths/mcp/__init__.py +5 -0
  69. wayfinder_paths/mcp/preview.py +185 -0
  70. wayfinder_paths/mcp/scripting.py +84 -0
  71. wayfinder_paths/mcp/server.py +52 -0
  72. wayfinder_paths/mcp/state/profile_store.py +195 -0
  73. wayfinder_paths/mcp/state/store.py +89 -0
  74. wayfinder_paths/mcp/test_scripting.py +267 -0
  75. wayfinder_paths/mcp/tools/__init__.py +0 -0
  76. wayfinder_paths/mcp/tools/balances.py +290 -0
  77. wayfinder_paths/mcp/tools/discovery.py +158 -0
  78. wayfinder_paths/mcp/tools/execute.py +770 -0
  79. wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
  80. wayfinder_paths/mcp/tools/quotes.py +288 -0
  81. wayfinder_paths/mcp/tools/run_script.py +286 -0
  82. wayfinder_paths/mcp/tools/strategies.py +188 -0
  83. wayfinder_paths/mcp/tools/tokens.py +46 -0
  84. wayfinder_paths/mcp/tools/wallets.py +354 -0
  85. wayfinder_paths/mcp/utils.py +129 -0
  86. wayfinder_paths/policies/hyperliquid.py +1 -1
  87. wayfinder_paths/policies/lifi.py +18 -0
  88. wayfinder_paths/policies/util.py +8 -2
  89. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +28 -119
  90. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
  91. wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
  92. wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
  93. wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
  94. wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
  95. wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
  96. wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
  97. wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
  98. wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
  99. wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
  100. wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
  101. wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
  102. wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
  103. wayfinder_paths/strategies/boros_hype_strategy/test_strategy.py +202 -0
  104. wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
  105. wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
  106. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +3 -12
  107. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +7 -29
  108. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +63 -40
  109. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
  110. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +0 -34
  111. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +11 -34
  112. wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
  113. wayfinder_paths/tests/test_test_coverage.py +1 -4
  114. wayfinder_paths-0.1.24.dist-info/METADATA +378 -0
  115. wayfinder_paths-0.1.24.dist-info/RECORD +185 -0
  116. {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
  117. wayfinder_paths/scripts/create_strategy.py +0 -139
  118. wayfinder_paths/scripts/make_wallets.py +0 -142
  119. wayfinder_paths-0.1.23.dist-info/METADATA +0 -354
  120. wayfinder_paths-0.1.23.dist-info/RECORD +0 -120
  121. /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
  122. {wayfinder_paths-0.1.23.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
@@ -0,0 +1,85 @@
1
+ """Boros-specific utilities (tick math, parsing helpers, conversions)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from decimal import Decimal
7
+ from typing import Any
8
+
9
+ BOROS_TICK_BASE = 1.0001
10
+
11
+
12
+ def tick_from_rate(
13
+ rate: float, tick_step: int, *, round_down: bool, base: float = BOROS_TICK_BASE
14
+ ) -> int:
15
+ """Convert APR rate to Boros limitTick."""
16
+ if tick_step <= 0:
17
+ tick_step = 1
18
+ ln_base = math.log(base)
19
+ if rate >= 0:
20
+ if rate == 0:
21
+ return 0
22
+ raw = math.log1p(rate) / (tick_step * ln_base)
23
+ return int(math.floor(raw) if round_down else math.ceil(raw))
24
+ # Negative rate
25
+ raw = math.log1p(-rate) / (tick_step * ln_base)
26
+ return -int(math.floor(raw) if round_down else math.ceil(raw))
27
+
28
+
29
+ def rate_from_tick(tick: int, tick_step: int, base: float = BOROS_TICK_BASE) -> float:
30
+ """Convert Boros limitTick to APR rate."""
31
+ if tick_step <= 0:
32
+ tick_step = 1
33
+ p = base ** (abs(int(tick)) * int(tick_step))
34
+ r = p - 1
35
+ return r if tick >= 0 else -r
36
+
37
+
38
+ def normalize_apr(value: Any) -> float | None:
39
+ """Normalize various APR encodings to decimal.
40
+
41
+ Handles: decimal (0.1115), percent (11.15), bps (1115), 1e18-scaled.
42
+ """
43
+ if value is None:
44
+ return None
45
+ try:
46
+ x = float(value)
47
+ except (TypeError, ValueError):
48
+ return None
49
+
50
+ if x == 0:
51
+ return None
52
+ # 1e18-scaled decimal
53
+ if x > 1e9:
54
+ return x / 1e18
55
+ # bps (1115 = 11.15%)
56
+ if x > 1000:
57
+ return x / 10_000.0
58
+ # percent (11.15 = 11.15%)
59
+ if x > 1:
60
+ return x / 100.0
61
+ # already decimal
62
+ return x
63
+
64
+
65
+ def cash_wei_to_float(value_wei: Any) -> float:
66
+ """Convert Boros cash units (1e18) to float."""
67
+ if value_wei is None:
68
+ return 0.0
69
+ try:
70
+ return float(Decimal(str(value_wei)) / Decimal(1e18))
71
+ except Exception:
72
+ return 0.0
73
+
74
+
75
+ def market_id_from_market_acc(market_acc: str) -> int | None:
76
+ """Parse a Boros `marketAcc` into a market_id (last 3 bytes)."""
77
+ if not market_acc or len(market_acc) < 8:
78
+ return None
79
+ try:
80
+ market_id = int(market_acc[-6:], 16)
81
+ except ValueError:
82
+ return None
83
+ if market_id == 0xFFFFFF:
84
+ return None
85
+ return market_id
@@ -61,7 +61,7 @@ class BRAPAdapter(BaseAdapter):
61
61
  owner_checksum = Web3.to_checksum_address(owner_address)
62
62
  spender_checksum = Web3.to_checksum_address(spender_address)
63
63
 
64
- if (chain_id, token_checksum.lower()) in TOKENS_REQUIRING_APPROVAL_RESET:
64
+ if (chain_id, token_checksum) in TOKENS_REQUIRING_APPROVAL_RESET:
65
65
  allowance = await get_token_allowance(
66
66
  token_checksum,
67
67
  chain_id,
@@ -0,0 +1,9 @@
1
+ schema_version: "0.1"
2
+ entrypoint: "adapters.brap_adapter.adapter.BRAPAdapter"
3
+ capabilities:
4
+ - "swap.quote"
5
+ - "swap.execute"
6
+ - "swap.compare_routes"
7
+ - "bridge.quote"
8
+ - "gas.estimate"
9
+ dependencies: []
@@ -1,10 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any
3
+ from typing import Any, Literal
4
4
 
5
5
  from eth_utils import to_checksum_address
6
6
 
7
+ from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
8
+ from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
7
9
  from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
10
+ from wayfinder_paths.core.adapters.models import LEND, UNLEND
8
11
  from wayfinder_paths.core.clients.HyperlendClient import (
9
12
  AssetsView,
10
13
  HyperlendClient,
@@ -36,28 +39,19 @@ class HyperlendAdapter(BaseAdapter):
36
39
  ) -> None:
37
40
  super().__init__("hyperlend_adapter", config)
38
41
  config = config or {}
39
- adapter_cfg = config.get("hyperlend_adapter") or {}
40
42
 
41
43
  self.strategy_wallet_signing_callback = strategy_wallet_signing_callback
42
44
  self.hyperlend_client = HyperlendClient()
43
45
 
46
+ self.ledger_adapter = LedgerAdapter()
47
+ self.token_adapter = TokenAdapter()
44
48
  strategy_wallet = config.get("strategy_wallet") or {}
45
49
  strategy_addr = strategy_wallet.get("address")
46
- if not strategy_addr:
47
- raise ValueError("strategy_wallet.address is required")
50
+
48
51
  self.strategy_wallet_address = to_checksum_address(strategy_addr)
49
- self.pool_address = to_checksum_address(
50
- adapter_cfg.get("pool_address") or HYPERLEND_POOL
51
- )
52
- self.gateway_address = to_checksum_address(
53
- adapter_cfg.get("wrapped_token_gateway") or HYPERLEND_WRAPPED_TOKEN_GATEWAY
54
- )
55
- self.wrapped_native = to_checksum_address(
56
- adapter_cfg.get("wrapped_native_underlying") or HYPEREVM_WHYPE
57
- )
58
- self.gateway_deposit_takes_pool = adapter_cfg.get(
59
- "gateway_deposit_takes_pool", True
60
- )
52
+ self.pool_address = HYPERLEND_POOL
53
+ self.gateway_address = HYPERLEND_WRAPPED_TOKEN_GATEWAY
54
+ self.wrapped_native = HYPEREVM_WHYPE
61
55
 
62
56
  async def get_stable_markets(
63
57
  self,
@@ -89,6 +83,46 @@ class HyperlendAdapter(BaseAdapter):
89
83
  except Exception as exc:
90
84
  return False, str(exc)
91
85
 
86
+ async def get_full_user_state(
87
+ self,
88
+ *,
89
+ account: str,
90
+ include_zero_positions: bool = False,
91
+ ) -> tuple[bool, dict[str, Any] | str]:
92
+ """
93
+ Full Hyperlend user state.
94
+
95
+ Backed by the Wayfinder API `hyperlend/assets/` response which already includes
96
+ per-asset supply/borrow balances and account health factor.
97
+ """
98
+ ok, view = await self.get_assets_view(user_address=account)
99
+ if not ok:
100
+ return False, str(view)
101
+
102
+ assets = view.get("assets", []) if isinstance(view, dict) else []
103
+ if include_zero_positions:
104
+ positions = assets
105
+ else:
106
+ positions = [
107
+ a
108
+ for a in assets
109
+ if float(a.get("supply", 0) or 0) > 0
110
+ or float(a.get("variable_borrow", 0) or 0) > 0
111
+ ]
112
+
113
+ return (
114
+ True,
115
+ {
116
+ "protocol": "hyperlend",
117
+ "account": account,
118
+ "positions": positions,
119
+ "accountData": view.get("account_data")
120
+ if isinstance(view, dict)
121
+ else {},
122
+ "assetsView": view,
123
+ },
124
+ )
125
+
92
126
  async def get_market_entry(
93
127
  self,
94
128
  *,
@@ -124,6 +158,7 @@ class HyperlendAdapter(BaseAdapter):
124
158
  qty: int,
125
159
  chain_id: int,
126
160
  native: bool = False,
161
+ strategy_name: str | None = None,
127
162
  ) -> tuple[bool, Any]:
128
163
  strategy = self.strategy_wallet_address
129
164
  qty = int(qty)
@@ -132,11 +167,12 @@ class HyperlendAdapter(BaseAdapter):
132
167
  chain_id = int(chain_id)
133
168
 
134
169
  if native:
135
- transcation = await self._encode_call(
170
+ token_addr = self.wrapped_native
171
+ transaction = await self._encode_call(
136
172
  target=self.gateway_address,
137
173
  abi=WRAPPED_TOKEN_GATEWAY_ABI,
138
174
  fn_name="depositETH",
139
- args=[self._gateway_first_arg(underlying_token), strategy, 0],
175
+ args=[self.pool_address, strategy, 0],
140
176
  from_address=strategy,
141
177
  chain_id=chain_id,
142
178
  value=qty,
@@ -153,7 +189,7 @@ class HyperlendAdapter(BaseAdapter):
153
189
  )
154
190
  if not approved[0]:
155
191
  return approved
156
- transcation = await self._encode_call(
192
+ transaction = await self._encode_call(
157
193
  target=self.pool_address,
158
194
  abi=POOL_ABI,
159
195
  fn_name="supply",
@@ -161,10 +197,22 @@ class HyperlendAdapter(BaseAdapter):
161
197
  from_address=strategy,
162
198
  chain_id=chain_id,
163
199
  )
200
+
164
201
  txn_hash = await send_transaction(
165
- transcation, self.strategy_wallet_signing_callback
202
+ transaction, self.strategy_wallet_signing_callback
166
203
  )
167
- return True, txn_hash
204
+
205
+ await self._record_pool_op(
206
+ token_address=token_addr,
207
+ amount=qty,
208
+ chain_id=chain_id,
209
+ wallet_address=strategy,
210
+ txn_hash=txn_hash,
211
+ strategy_name=strategy_name,
212
+ op_type="lend",
213
+ )
214
+
215
+ return (True, txn_hash)
168
216
 
169
217
  async def unlend(
170
218
  self,
@@ -173,6 +221,7 @@ class HyperlendAdapter(BaseAdapter):
173
221
  qty: int,
174
222
  chain_id: int,
175
223
  native: bool = False,
224
+ strategy_name: str | None = None,
176
225
  ) -> tuple[bool, Any]:
177
226
  strategy = self.strategy_wallet_address
178
227
  qty = int(qty)
@@ -181,11 +230,12 @@ class HyperlendAdapter(BaseAdapter):
181
230
  chain_id = int(chain_id)
182
231
 
183
232
  if native:
233
+ token_addr = self.wrapped_native
184
234
  transaction = await self._encode_call(
185
235
  target=self.gateway_address,
186
236
  abi=WRAPPED_TOKEN_GATEWAY_ABI,
187
237
  fn_name="withdrawETH",
188
- args=[self._gateway_first_arg(underlying_token), qty, strategy],
238
+ args=[self.pool_address, qty, strategy],
189
239
  from_address=strategy,
190
240
  chain_id=chain_id,
191
241
  )
@@ -199,9 +249,20 @@ class HyperlendAdapter(BaseAdapter):
199
249
  from_address=strategy,
200
250
  chain_id=chain_id,
201
251
  )
252
+
202
253
  txn_hash = await send_transaction(
203
254
  transaction, self.strategy_wallet_signing_callback
204
255
  )
256
+ await self._record_pool_op(
257
+ token_address=token_addr,
258
+ amount=qty,
259
+ chain_id=chain_id,
260
+ wallet_address=strategy,
261
+ txn_hash=txn_hash,
262
+ strategy_name=strategy_name,
263
+ op_type="unlend",
264
+ )
265
+
205
266
  return True, txn_hash
206
267
 
207
268
  async def _encode_call(
@@ -235,7 +296,81 @@ class HyperlendAdapter(BaseAdapter):
235
296
  }
236
297
  return transaction
237
298
 
238
- def _gateway_first_arg(self, underlying_token: str) -> str:
239
- if self.gateway_deposit_takes_pool:
240
- return self.pool_address
241
- return to_checksum_address(underlying_token)
299
+ async def _record_pool_op(
300
+ self,
301
+ token_address: str,
302
+ amount: int,
303
+ chain_id: int,
304
+ wallet_address: str,
305
+ txn_hash: str,
306
+ op_type: Literal["lend", "unlend"],
307
+ strategy_name: str | None = None,
308
+ ):
309
+ amount_usd = await self._calculate_amount_usd(
310
+ token_address=token_address,
311
+ amount=amount,
312
+ chain_id=chain_id,
313
+ )
314
+
315
+ model = {"lend": LEND, "unlend": UNLEND}[op_type]
316
+
317
+ operation_data = model(
318
+ adapter=self.adapter_type,
319
+ token_address=token_address,
320
+ pool_address=self.pool_address,
321
+ amount=str(amount),
322
+ amount_usd=amount_usd or 0,
323
+ transaction_hash=txn_hash,
324
+ transaction_chain_id=chain_id,
325
+ )
326
+
327
+ success, ledger_response = await self.ledger_adapter.record_operation(
328
+ wallet_address=wallet_address,
329
+ operation_data=operation_data,
330
+ usd_value=amount_usd or 0,
331
+ strategy_name=strategy_name,
332
+ )
333
+ if not success:
334
+ self.logger.warning("Ledger record failed", error=ledger_response)
335
+
336
+ async def _calculate_amount_usd(
337
+ self,
338
+ token_address: str,
339
+ amount: int,
340
+ chain_id: int,
341
+ ) -> float | None:
342
+ # Get token details with market data using the address as query
343
+ success, token_data = await self.token_adapter.get_token(
344
+ query=token_address,
345
+ chain_id=chain_id,
346
+ )
347
+ if not success or not token_data:
348
+ self.logger.warning(
349
+ f"Could not get token info for {token_address} on chain {chain_id}"
350
+ )
351
+ return None
352
+
353
+ # Get price data
354
+ decimals, current_price = (
355
+ token_data["decimals"],
356
+ token_data["current_price"],
357
+ )
358
+ return current_price * float(amount) / 10 ** int(decimals)
359
+
360
+ async def _ensure_allowance(
361
+ self,
362
+ *,
363
+ token_address: str,
364
+ owner: str,
365
+ spender: str,
366
+ amount: int,
367
+ chain_id: int,
368
+ ) -> tuple[bool, Any]:
369
+ return await ensure_allowance(
370
+ token_address=token_address,
371
+ owner=owner,
372
+ spender=spender,
373
+ amount=amount,
374
+ chain_id=chain_id,
375
+ signing_callback=self.strategy_wallet_signing_callback,
376
+ )
@@ -0,0 +1,9 @@
1
+ schema_version: "0.1"
2
+ entrypoint: "adapters.hyperlend_adapter.adapter.HyperlendAdapter"
3
+ capabilities:
4
+ - "market.stable_markets"
5
+ - "market.assets_view"
6
+ - "market.rate_history"
7
+ - "lending.lend"
8
+ - "lending.unlend"
9
+ dependencies: []
@@ -151,19 +151,6 @@ class TestHyperlendAdapter:
151
151
  def test_adapter_type(self, adapter):
152
152
  assert adapter.adapter_type == "HYPERLEND"
153
153
 
154
- @pytest.mark.asyncio
155
- async def test_health_check(self, adapter):
156
- health = await adapter.health_check()
157
- assert isinstance(health, dict)
158
- assert health.get("status") in {"healthy", "unhealthy", "error"}
159
- assert health.get("adapter") == "HYPERLEND"
160
-
161
- @pytest.mark.asyncio
162
- async def test_connect(self, adapter):
163
- ok = await adapter.connect()
164
- assert isinstance(ok, bool)
165
- assert ok is True
166
-
167
154
  @pytest.mark.asyncio
168
155
  async def test_get_stable_markets_with_is_stable_symbol(
169
156
  self, adapter, mock_hyperlend_client
@@ -271,6 +258,83 @@ class TestHyperlendAdapter:
271
258
  assert success is False
272
259
  assert "API Error: Invalid address" in data
273
260
 
261
+ @pytest.mark.asyncio
262
+ async def test_get_full_user_state_filters_zero_positions(
263
+ self, adapter, mock_hyperlend_client
264
+ ):
265
+ mock_response = {
266
+ "block_number": 12345,
267
+ "user": "0xabc",
268
+ "native_balance_wei": 0,
269
+ "native_balance": 0.0,
270
+ "assets": [
271
+ {
272
+ "underlying": "0x1",
273
+ "symbol": "USDC",
274
+ "symbol_canonical": "usdc",
275
+ "symbol_display": "USDC",
276
+ "decimals": 6,
277
+ "a_token": "0xa",
278
+ "variable_debt_token": "0xd",
279
+ "usage_as_collateral_enabled": True,
280
+ "borrowing_enabled": True,
281
+ "is_active": True,
282
+ "is_frozen": False,
283
+ "is_paused": False,
284
+ "is_siloed_borrowing": False,
285
+ "is_stablecoin": True,
286
+ "underlying_wallet_balance": 0.0,
287
+ "underlying_wallet_balance_wei": 0,
288
+ "price_usd": 1.0,
289
+ "supply": 0.0,
290
+ "variable_borrow": 0.0,
291
+ "supply_usd": 0.0,
292
+ "variable_borrow_usd": 0.0,
293
+ "supply_apr": 0.0,
294
+ "supply_apy": 0.0,
295
+ "variable_borrow_apr": 0.0,
296
+ "variable_borrow_apy": 0.0,
297
+ },
298
+ {
299
+ "underlying": "0x2",
300
+ "symbol": "USDT",
301
+ "symbol_canonical": "usdt",
302
+ "symbol_display": "USDT",
303
+ "decimals": 6,
304
+ "a_token": "0xa2",
305
+ "variable_debt_token": "0xd2",
306
+ "usage_as_collateral_enabled": True,
307
+ "borrowing_enabled": True,
308
+ "is_active": True,
309
+ "is_frozen": False,
310
+ "is_paused": False,
311
+ "is_siloed_borrowing": False,
312
+ "is_stablecoin": True,
313
+ "underlying_wallet_balance": 0.0,
314
+ "underlying_wallet_balance_wei": 0,
315
+ "price_usd": 1.0,
316
+ "supply": 123.0,
317
+ "variable_borrow": 0.0,
318
+ "supply_usd": 123.0,
319
+ "variable_borrow_usd": 0.0,
320
+ "supply_apr": 0.05,
321
+ "supply_apy": 0.05,
322
+ "variable_borrow_apr": 0.07,
323
+ "variable_borrow_apy": 0.07,
324
+ },
325
+ ],
326
+ "account_data": {"health_factor": 2.0},
327
+ "base_currency_info": {},
328
+ }
329
+ mock_hyperlend_client.get_assets_view = AsyncMock(return_value=mock_response)
330
+
331
+ ok, state = await adapter.get_full_user_state(account="0xabc")
332
+ assert ok is True
333
+ assert state["protocol"] == "hyperlend"
334
+ assert state["account"] == "0xabc"
335
+ assert len(state["positions"]) == 1
336
+ assert state["positions"][0]["symbol"] == "USDT"
337
+
274
338
  @pytest.mark.asyncio
275
339
  async def test_get_assets_view_empty_response(self, adapter, mock_hyperlend_client):
276
340
  mock_response = {
@@ -1,18 +1,11 @@
1
- from .adapter import (
2
- ARBITRUM_USDC_ADDRESS,
3
- HYPERLIQUID_BRIDGE_ADDRESS,
4
- HyperliquidAdapter,
5
- )
6
- from .executor import HyperliquidExecutor, LocalHyperliquidExecutor
1
+ from .adapter import HyperliquidAdapter
2
+ from .executor import LocalHyperliquidExecutor
7
3
  from .paired_filler import FillConfig, FillConfirmCfg, PairedFiller
8
4
 
9
5
  __all__ = [
10
6
  "HyperliquidAdapter",
11
- "HyperliquidExecutor",
12
7
  "LocalHyperliquidExecutor",
13
8
  "PairedFiller",
14
9
  "FillConfig",
15
10
  "FillConfirmCfg",
16
- "HYPERLIQUID_BRIDGE_ADDRESS",
17
- "ARBITRUM_USDC_ADDRESS",
18
11
  ]