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,255 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # TOKEN IDS (wayfinder token identifiers)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+
5
+ # Arbitrum tokens
6
+ USDC_ARB = "usd-coin-arbitrum"
7
+ USDT_ARB = "usdt0-arbitrum"
8
+ ETH_ARB = "ethereum-arbitrum"
9
+
10
+ # HyperEVM tokens
11
+ HYPE_NATIVE = "hype-hyperevm"
12
+ WHYPE = "wrapped-hype-hyperevm" # Wrapped HYPE (1:1 with native HYPE)
13
+ KHYPE_LST = "kinetic-staked-hype-hyperevm"
14
+ LOOPED_HYPE = "looped-hype-hyperevm"
15
+ USDC_HYPE = "usd-coin-hyperevm"
16
+
17
+
18
+ # ─────────────────────────────────────────────────────────────────────────────
19
+ # CONTRACT ADDRESSES
20
+ # ─────────────────────────────────────────────────────────────────────────────
21
+
22
+ # HyperEVM token addresses
23
+ KHYPE_ADDRESS = "0xfD739d4e423301CE9385c1fb8850539D657C296D"
24
+ LOOPED_HYPE_ADDRESS = "0x5748ae796AE46A4F1348a1693de4b50560485562"
25
+ WHYPE_ADDRESS = "0x5555555555555555555555555555555555555555"
26
+
27
+ # Accountant contracts for exchange rate calculations
28
+ KHYPE_STAKING_ACCOUNTANT = "0x9209648Ec9D448EF57116B73A2f081835643dc7A"
29
+ LHYPE_ACCOUNTANT = "0xcE621a3CA6F72706678cFF0572ae8d15e5F001c3"
30
+
31
+ # Chain IDs
32
+ HYPEREVM_CHAIN_ID = 999
33
+ ARBITRUM_CHAIN_ID = 42161
34
+
35
+ # ABIs for exchange rate reads
36
+ KHYPE_STAKING_ACCOUNTANT_ABI = [
37
+ {
38
+ "inputs": [
39
+ {"internalType": "uint256", "name": "kHYPEAmount", "type": "uint256"}
40
+ ],
41
+ "name": "kHYPEToHYPE",
42
+ "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
43
+ "stateMutability": "view",
44
+ "type": "function",
45
+ }
46
+ ]
47
+
48
+ LHYPE_ACCOUNTANT_ABI = [
49
+ {
50
+ "inputs": [{"internalType": "address", "name": "quote", "type": "address"}],
51
+ "name": "getRateInQuote",
52
+ "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
53
+ "stateMutability": "view",
54
+ "type": "function",
55
+ }
56
+ ]
57
+
58
+ # WHYPE contract ABI for unwrapping (standard WETH-like interface)
59
+ WHYPE_ABI = [
60
+ {
61
+ "name": "withdraw",
62
+ "type": "function",
63
+ "stateMutability": "nonpayable",
64
+ "inputs": [{"name": "wad", "type": "uint256"}],
65
+ "outputs": [],
66
+ },
67
+ {
68
+ "name": "balanceOf",
69
+ "type": "function",
70
+ "stateMutability": "view",
71
+ "inputs": [{"name": "owner", "type": "address"}],
72
+ "outputs": [{"type": "uint256"}],
73
+ },
74
+ ]
75
+
76
+
77
+ # ─────────────────────────────────────────────────────────────────────────────
78
+ # STRATEGY THRESHOLDS
79
+ # ─────────────────────────────────────────────────────────────────────────────
80
+
81
+ MIN_NET_DEPOSIT = 150.0 # Minimum deposit to activate strategy
82
+ MAX_HL_LEVERAGE = 2.0 # Maximum leverage on Hyperliquid shorts
83
+ PARTIAL_TRIM_THRESHOLD = 0.75 # Risk level to trigger partial trim
84
+ FULL_REBALANCE_THRESHOLD = 0.90 # Risk level to trigger full rebalance
85
+ ALLOCATION_DEVIATION_THRESHOLD = 0.03 # 3% deviation triggers rebalance
86
+ HORIZON_DAYS = 7 # Planning horizon in days
87
+
88
+
89
+ # ─────────────────────────────────────────────────────────────────────────────
90
+ # BOROS CONFIGURATION
91
+ # ─────────────────────────────────────────────────────────────────────────────
92
+
93
+ # NOTE: Boros migrated the old USDT-collateralized HYPE market. The current
94
+ # entry path uses HYPE collateral (via the LayerZero OFT token on Arbitrum).
95
+ BOROS_HYPE_MARKET_ID = 51 # HYPERLIQUID-HYPE-27FEB2026 (fallback)
96
+ BOROS_HYPE_TOKEN_ID = 5 # HYPE collateral token ID on Boros (current)
97
+ BOROS_MIN_DEPOSIT_USD = 10.50 # Minimum collateral value to bother funding
98
+ BOROS_MIN_TENOR_DAYS = 3 # Roll to new market if < 3 days to expiry
99
+ BOROS_ENABLE_MIN_TOTAL_USD = 80.0 # Skip Boros if capital below this
100
+
101
+ # LayerZero OFT bridge (HyperEVM native HYPE → Arbitrum OFT HYPE)
102
+ HYPE_OFT_ADDRESS = "0x007C26Ed5C33Fe6fEF62223d4c363A01F1b1dDc1"
103
+ LZ_EID_ARBITRUM = 30110
104
+
105
+ # Minimal IOFT ABI for quoting + sending.
106
+ HYPE_OFT_ABI = [
107
+ {
108
+ "inputs": [
109
+ {
110
+ "components": [
111
+ {"internalType": "uint32", "name": "dstEid", "type": "uint32"},
112
+ {"internalType": "bytes32", "name": "to", "type": "bytes32"},
113
+ {"internalType": "uint256", "name": "amountLD", "type": "uint256"},
114
+ {
115
+ "internalType": "uint256",
116
+ "name": "minAmountLD",
117
+ "type": "uint256",
118
+ },
119
+ {
120
+ "internalType": "bytes",
121
+ "name": "extraOptions",
122
+ "type": "bytes",
123
+ },
124
+ {"internalType": "bytes", "name": "composeMsg", "type": "bytes"},
125
+ {"internalType": "bytes", "name": "oftCmd", "type": "bytes"},
126
+ ],
127
+ "internalType": "struct SendParam",
128
+ "name": "_sendParam",
129
+ "type": "tuple",
130
+ },
131
+ {"internalType": "bool", "name": "_payInLzToken", "type": "bool"},
132
+ ],
133
+ "name": "quoteSend",
134
+ "outputs": [
135
+ {
136
+ "components": [
137
+ {"internalType": "uint256", "name": "nativeFee", "type": "uint256"},
138
+ {
139
+ "internalType": "uint256",
140
+ "name": "lzTokenFee",
141
+ "type": "uint256",
142
+ },
143
+ ],
144
+ "internalType": "struct MessagingFee",
145
+ "name": "",
146
+ "type": "tuple",
147
+ }
148
+ ],
149
+ "stateMutability": "view",
150
+ "type": "function",
151
+ },
152
+ {
153
+ "inputs": [
154
+ {
155
+ "components": [
156
+ {"internalType": "uint32", "name": "dstEid", "type": "uint32"},
157
+ {"internalType": "bytes32", "name": "to", "type": "bytes32"},
158
+ {"internalType": "uint256", "name": "amountLD", "type": "uint256"},
159
+ {
160
+ "internalType": "uint256",
161
+ "name": "minAmountLD",
162
+ "type": "uint256",
163
+ },
164
+ {
165
+ "internalType": "bytes",
166
+ "name": "extraOptions",
167
+ "type": "bytes",
168
+ },
169
+ {"internalType": "bytes", "name": "composeMsg", "type": "bytes"},
170
+ {"internalType": "bytes", "name": "oftCmd", "type": "bytes"},
171
+ ],
172
+ "internalType": "struct SendParam",
173
+ "name": "_sendParam",
174
+ "type": "tuple",
175
+ },
176
+ {
177
+ "components": [
178
+ {"internalType": "uint256", "name": "nativeFee", "type": "uint256"},
179
+ {
180
+ "internalType": "uint256",
181
+ "name": "lzTokenFee",
182
+ "type": "uint256",
183
+ },
184
+ ],
185
+ "internalType": "struct MessagingFee",
186
+ "name": "_fee",
187
+ "type": "tuple",
188
+ },
189
+ {"internalType": "address", "name": "_refundAddress", "type": "address"},
190
+ ],
191
+ "name": "send",
192
+ "outputs": [
193
+ {
194
+ "components": [
195
+ {"internalType": "bytes32", "name": "guid", "type": "bytes32"},
196
+ {"internalType": "uint64", "name": "nonce", "type": "uint64"},
197
+ {
198
+ "components": [
199
+ {
200
+ "internalType": "uint256",
201
+ "name": "nativeFee",
202
+ "type": "uint256",
203
+ },
204
+ {
205
+ "internalType": "uint256",
206
+ "name": "lzTokenFee",
207
+ "type": "uint256",
208
+ },
209
+ ],
210
+ "internalType": "struct MessagingFee",
211
+ "name": "fee",
212
+ "type": "tuple",
213
+ },
214
+ ],
215
+ "internalType": "struct MessagingReceipt",
216
+ "name": "",
217
+ "type": "tuple",
218
+ },
219
+ {
220
+ "components": [
221
+ {
222
+ "internalType": "uint256",
223
+ "name": "amountSentLD",
224
+ "type": "uint256",
225
+ },
226
+ {
227
+ "internalType": "uint256",
228
+ "name": "amountReceivedLD",
229
+ "type": "uint256",
230
+ },
231
+ ],
232
+ "internalType": "struct OFTReceipt",
233
+ "name": "",
234
+ "type": "tuple",
235
+ },
236
+ ],
237
+ "stateMutability": "payable",
238
+ "type": "function",
239
+ },
240
+ ]
241
+
242
+ # ─────────────────────────────────────────────────────────────────────────────
243
+ # GAS CONFIGURATION
244
+ # ─────────────────────────────────────────────────────────────────────────────
245
+
246
+ MIN_HYPE_GAS = 0.1 # Minimum HYPE for HyperEVM gas
247
+ HYPE_DEPOSIT_REQUIREMENT = 0.1 # HYPE to ensure for gas on deposit
248
+
249
+
250
+ # ─────────────────────────────────────────────────────────────────────────────
251
+ # EXTERNAL API ENDPOINTS
252
+ # ─────────────────────────────────────────────────────────────────────────────
253
+
254
+ LHYPE_API_URL = "https://app.loopingcollective.org/api/external/asset/lhype"
255
+ KHYPE_API_URL = "https://kinetiq.xyz/api/khype"
@@ -0,0 +1,37 @@
1
+ {
2
+ "smoke": {
3
+ "deposit": {
4
+ "main_token_amount": 200.0,
5
+ "gas_token_amount": 0.01
6
+ },
7
+ "update": {},
8
+ "withdraw": {}
9
+ },
10
+ "canonical_deposit": {
11
+ "deposit": {
12
+ "main_token_amount": 500.0,
13
+ "gas_token_amount": 0.02
14
+ }
15
+ },
16
+ "canonical_update": {
17
+ "update": {}
18
+ },
19
+ "canonical_status": {
20
+ "status": {}
21
+ },
22
+ "minimum_deposit": {
23
+ "deposit": {
24
+ "main_token_amount": 150.0,
25
+ "gas_token_amount": 0.01
26
+ }
27
+ },
28
+ "error_below_minimum": {
29
+ "deposit": {
30
+ "main_token_amount": 50.0,
31
+ "gas_token_amount": 0.01
32
+ },
33
+ "expect": {
34
+ "success": false
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,114 @@
1
+ """
2
+ HyperEVM / spot-leg operations for BorosHypeStrategy.
3
+
4
+ Kept as a mixin so the main strategy file stays readable without changing behavior.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from .constants import HYPE_NATIVE, KHYPE_LST, LOOPED_HYPE, MIN_HYPE_GAS
12
+ from .types import Inventory
13
+
14
+
15
+ class BorosHypeHyperEvmOpsMixin:
16
+ async def _ensure_gas_on_hyperevm(
17
+ self, params: dict[str, Any], inventory: Inventory
18
+ ) -> tuple[bool, str]:
19
+ min_hype = float(params.get("min_hype") or MIN_HYPE_GAS)
20
+ need = max(0.0, min_hype - inventory.hype_hyperevm_balance)
21
+ if need <= 0.0:
22
+ return True, "HyperEVM gas already sufficient"
23
+
24
+ # Best-effort: if HYPE exists on HL spot, bridge it over.
25
+ if inventory.hl_spot_hype > max(0.1, need + 0.001):
26
+ return await self._transfer_hl_spot_to_hyperevm(
27
+ {"hype_amount": max(0.1, need)}, inventory
28
+ )
29
+
30
+ # Otherwise, we expect the upcoming BRIDGE_TO_HYPEREVM routine to bring HYPE.
31
+ return True, "HyperEVM gas will be provisioned during routing"
32
+
33
+ async def _ensure_gas_on_arbitrum(
34
+ self, params: dict[str, Any], inventory: Inventory
35
+ ) -> tuple[bool, str]:
36
+ # TODO: Implement - bridge ETH or use gas station
37
+ return True, "Arbitrum gas routing not yet implemented"
38
+
39
+ async def _swap_hype_to_lst(
40
+ self, params: dict[str, Any], inventory: Inventory
41
+ ) -> tuple[bool, str]:
42
+ hype_amount = params.get("hype_amount", 0.0)
43
+
44
+ if hype_amount <= 0:
45
+ return True, "No HYPE to swap"
46
+
47
+ if self.simulation:
48
+ return True, f"[SIMULATION] Swapped {hype_amount:.4f} HYPE to LSTs"
49
+
50
+ if not self.brap_adapter:
51
+ return False, "BRAP adapter not configured"
52
+
53
+ strategy_wallet = self._config.get("strategy_wallet", {})
54
+ wallet_address = strategy_wallet.get("address")
55
+ if not wallet_address:
56
+ return False, "No strategy wallet address configured"
57
+
58
+ # Normalize split fractions.
59
+ khype_fraction = max(0.0, float(self.hedge_cfg.khype_fraction))
60
+ looped_fraction = max(0.0, float(self.hedge_cfg.looped_hype_fraction))
61
+ total_fraction = khype_fraction + looped_fraction
62
+ if total_fraction <= 0:
63
+ khype_fraction = 0.5
64
+ looped_fraction = 0.5
65
+ total_fraction = 1.0
66
+
67
+ khype_share = khype_fraction / total_fraction
68
+
69
+ # HYPE native has 18 decimals.
70
+ hype_amount_wei = int(float(hype_amount) * 1e18)
71
+ khype_amount_wei = int(hype_amount_wei * khype_share)
72
+ looped_amount_wei = max(0, hype_amount_wei - khype_amount_wei)
73
+
74
+ # If one side is dust, allocate to the other.
75
+ min_swap_wei = int(0.02 * 1e18)
76
+ if 0 < khype_amount_wei < min_swap_wei:
77
+ looped_amount_wei += khype_amount_wei
78
+ khype_amount_wei = 0
79
+ if 0 < looped_amount_wei < min_swap_wei:
80
+ khype_amount_wei += looped_amount_wei
81
+ looped_amount_wei = 0
82
+
83
+ results: list[str] = []
84
+
85
+ if khype_amount_wei > 0:
86
+ ok, res = await self.brap_adapter.swap_from_token_ids(
87
+ from_token_id=HYPE_NATIVE,
88
+ to_token_id=KHYPE_LST,
89
+ from_address=wallet_address,
90
+ amount=str(khype_amount_wei),
91
+ slippage=0.01,
92
+ strategy_name="boros_hype_strategy",
93
+ )
94
+ if not ok:
95
+ return False, f"Swap HYPE→kHYPE failed: {res}"
96
+ results.append(f"kHYPE({khype_amount_wei / 1e18:.4f} HYPE)")
97
+
98
+ if looped_amount_wei > 0:
99
+ ok, res = await self.brap_adapter.swap_from_token_ids(
100
+ from_token_id=HYPE_NATIVE,
101
+ to_token_id=LOOPED_HYPE,
102
+ from_address=wallet_address,
103
+ amount=str(looped_amount_wei),
104
+ slippage=0.01,
105
+ strategy_name="boros_hype_strategy",
106
+ )
107
+ if not ok:
108
+ return False, f"Swap HYPE→looped HYPE failed: {res}"
109
+ results.append(f"looped({looped_amount_wei / 1e18:.4f} HYPE)")
110
+
111
+ if not results:
112
+ return True, "No non-dust HYPE amount to swap"
113
+
114
+ return True, f"Swapped HYPE → {', '.join(results)}"