wayfinder-paths 0.1.15__py3-none-any.whl → 0.1.16__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 (47) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +19 -20
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +66 -37
  3. wayfinder_paths/adapters/balance_adapter/test_adapter.py +2 -8
  4. wayfinder_paths/adapters/brap_adapter/README.md +22 -19
  5. wayfinder_paths/adapters/brap_adapter/adapter.py +33 -34
  6. wayfinder_paths/adapters/brap_adapter/test_adapter.py +2 -18
  7. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +40 -56
  8. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +1 -8
  9. wayfinder_paths/adapters/moonwell_adapter/README.md +29 -31
  10. wayfinder_paths/adapters/moonwell_adapter/adapter.py +301 -662
  11. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +275 -179
  12. wayfinder_paths/core/config.py +8 -47
  13. wayfinder_paths/core/constants/base.py +0 -1
  14. wayfinder_paths/core/constants/erc20_abi.py +13 -13
  15. wayfinder_paths/core/strategies/Strategy.py +6 -2
  16. wayfinder_paths/core/utils/erc20_service.py +100 -0
  17. wayfinder_paths/core/utils/evm_helpers.py +1 -1
  18. wayfinder_paths/core/utils/transaction.py +191 -0
  19. wayfinder_paths/core/utils/web3.py +66 -0
  20. wayfinder_paths/run_strategy.py +37 -6
  21. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +200 -224
  22. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +128 -151
  23. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -1
  24. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +52 -78
  25. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -12
  26. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +0 -1
  27. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +39 -64
  28. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -1
  29. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +42 -85
  30. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -8
  31. wayfinder_paths/templates/strategy/README.md +1 -5
  32. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/METADATA +3 -41
  33. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/RECORD +35 -44
  34. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/WHEEL +1 -1
  35. wayfinder_paths/core/clients/sdk_example.py +0 -125
  36. wayfinder_paths/core/engine/__init__.py +0 -5
  37. wayfinder_paths/core/services/__init__.py +0 -0
  38. wayfinder_paths/core/services/base.py +0 -131
  39. wayfinder_paths/core/services/local_evm_txn.py +0 -350
  40. wayfinder_paths/core/services/local_token_txn.py +0 -238
  41. wayfinder_paths/core/services/web3_service.py +0 -43
  42. wayfinder_paths/core/wallets/README.md +0 -88
  43. wayfinder_paths/core/wallets/WalletManager.py +0 -56
  44. wayfinder_paths/core/wallets/__init__.py +0 -7
  45. wayfinder_paths/scripts/run_strategy.py +0 -152
  46. wayfinder_paths/strategies/config.py +0 -85
  47. {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/LICENSE +0 -0
@@ -143,7 +143,8 @@ class TestBasisTradingStrategy:
143
143
  return_value=(True, 100.0) # (deposit_confirmed, final_balance)
144
144
  )
145
145
  mock.wait_for_withdrawal = AsyncMock(
146
- return_value=(True, {"0x123456": 100.0}) # tx_hash -> amount (float)
146
+ # tx_hash -> amount (float)
147
+ return_value=(True, {"0x123456": 100.0})
147
148
  )
148
149
  return mock
149
150
 
@@ -170,68 +171,53 @@ class TestBasisTradingStrategy:
170
171
  "wayfinder_paths.strategies.basis_trading_strategy.strategy.LedgerAdapter",
171
172
  return_value=ledger_adapter,
172
173
  ):
173
- with patch(
174
- "wayfinder_paths.strategies.basis_trading_strategy.strategy.WalletManager"
175
- ):
176
- from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
177
- BasisTradingStrategy,
178
- )
179
-
180
- s = BasisTradingStrategy(
181
- config={
182
- "main_wallet": {"address": "0x1234"},
183
- "strategy_wallet": {"address": "0x5678"},
184
- },
185
- )
186
- s.hyperliquid_adapter = mock_hyperliquid_adapter
187
- s.ledger_adapter = ledger_adapter
188
- s.balance_adapter = MagicMock()
189
- s.balance_adapter.get_balance = AsyncMock(
190
- return_value=(True, 0)
191
- )
192
- s.balance_adapter.move_from_main_wallet_to_strategy_wallet = AsyncMock(
193
- return_value=(True, {})
194
- )
195
- s.balance_adapter.move_from_strategy_wallet_to_main_wallet = AsyncMock(
196
- return_value=(True, {})
197
- )
198
- s.balance_adapter.send_to_address = AsyncMock(
199
- return_value=(True, {"tx_hash": "0x123"})
200
- )
201
- # Mock internal dependencies to prevent MagicMock await errors
202
- # These are needed if the real method somehow gets called
203
- s.balance_adapter.token_client = AsyncMock()
204
- s.balance_adapter.token_client.get_token_details = (
205
- AsyncMock(
206
- return_value={
207
- "id": "usdc",
208
- "address": "0x1234",
209
- "decimals": 6,
210
- }
211
- )
212
- )
213
- s.balance_adapter.token_transactions = AsyncMock()
214
- s.balance_adapter.token_transactions.build_send = AsyncMock(
215
- return_value=(True, {"transaction": "0xMOCK"})
216
- )
217
- s.balance_adapter.wallet_provider = AsyncMock()
218
- s.balance_adapter.wallet_provider.broadcast_transaction = (
219
- AsyncMock(
220
- return_value=(True, {"transaction_hash": "0x123"})
221
- )
222
- )
223
- s.balance_adapter.token_adapter = AsyncMock()
224
- s.balance_adapter.token_adapter.get_token_price = AsyncMock(
225
- return_value=(True, {"current_price": 1.0})
226
- )
227
- # ledger_adapter is real, but ensure its methods are async-mockable
228
- s.balance_adapter.ledger_adapter = ledger_adapter
229
- # Also ensure the balance_adapter's _move_between_wallets won't call real methods
230
- # by making sure all its dependencies return AsyncMock
231
- s.balance_adapter._move_between_wallets = AsyncMock(
232
- return_value=(True, {"transaction_hash": "0x123"})
233
- )
234
- return s
174
+ from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
175
+ BasisTradingStrategy,
176
+ )
177
+
178
+ s = BasisTradingStrategy(
179
+ config={
180
+ "main_wallet": {"address": "0x1234"},
181
+ "strategy_wallet": {"address": "0x5678"},
182
+ },
183
+ )
184
+ s.hyperliquid_adapter = mock_hyperliquid_adapter
185
+ s.ledger_adapter = ledger_adapter
186
+ s.balance_adapter = MagicMock()
187
+ s.balance_adapter.get_balance = AsyncMock(
188
+ return_value=(True, 0)
189
+ )
190
+ s.balance_adapter.move_from_main_wallet_to_strategy_wallet = (
191
+ AsyncMock(return_value=(True, {}))
192
+ )
193
+ s.balance_adapter.move_from_strategy_wallet_to_main_wallet = (
194
+ AsyncMock(return_value=(True, {}))
195
+ )
196
+ s.balance_adapter.send_to_address = AsyncMock(
197
+ return_value=(True, {"tx_hash": "0x123"})
198
+ )
199
+ # Mock internal dependencies to prevent MagicMock await errors
200
+ # These are needed if the real method somehow gets called
201
+ s.balance_adapter.token_client = AsyncMock()
202
+ s.balance_adapter.token_client.get_token_details = AsyncMock(
203
+ return_value={
204
+ "id": "usdc",
205
+ "address": "0x1234",
206
+ "decimals": 6,
207
+ }
208
+ )
209
+ s.balance_adapter.token_adapter = AsyncMock()
210
+ s.balance_adapter.token_adapter.get_token_price = AsyncMock(
211
+ return_value=(True, {"current_price": 1.0})
212
+ )
213
+ # ledger_adapter is real, but ensure its methods are async-mockable
214
+ s.balance_adapter.ledger_adapter = ledger_adapter
215
+ # Also ensure the balance_adapter's _move_between_wallets won't call real methods
216
+ # by making sure all its dependencies return AsyncMock
217
+ s.balance_adapter._move_between_wallets = AsyncMock(
218
+ return_value=(True, {"transaction_hash": "0x123"})
219
+ )
220
+ return s
235
221
 
236
222
  @pytest.mark.asyncio
237
223
  @pytest.mark.smoke
@@ -303,7 +289,7 @@ class TestBasisTradingStrategy:
303
289
 
304
290
  success, msg = await strategy.update()
305
291
  assert success is False
306
- assert "No deposit" in msg
292
+ assert "No funds to manage" in msg
307
293
 
308
294
  @pytest.mark.asyncio
309
295
  async def test_withdraw_without_deposit(self, strategy):
@@ -584,38 +570,35 @@ class TestBasisTradingStrategy:
584
570
  "wayfinder_paths.strategies.basis_trading_strategy.strategy.LedgerAdapter",
585
571
  return_value=ledger_adapter,
586
572
  ):
587
- with patch(
588
- "wayfinder_paths.strategies.basis_trading_strategy.strategy.WalletManager"
589
- ):
590
- from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
591
- BasisTradingStrategy,
592
- )
593
-
594
- s = BasisTradingStrategy(
595
- config={
596
- "main_wallet": {"address": "0x1234"},
597
- "strategy_wallet": {"address": "0x5678"},
598
- },
599
- )
600
- s.hyperliquid_adapter = mock_hyperliquid_adapter
601
- s.ledger_adapter = ledger_adapter
602
-
603
- # Mock get_max_builder_fee returning sufficient approval
604
- mock_hyperliquid_adapter.get_max_builder_fee = AsyncMock(
605
- return_value=(
606
- True,
607
- 30,
608
- ) # Already approved for 30 tenths bp
609
- )
610
- mock_hyperliquid_adapter.approve_builder_fee = AsyncMock(
611
- return_value=(True, {"status": "ok"})
612
- )
613
-
614
- success, msg = await s.ensure_builder_fee_approved()
615
- assert success
616
- assert "already approved" in msg.lower()
617
- # Should not have called approve_builder_fee
618
- mock_hyperliquid_adapter.approve_builder_fee.assert_not_called()
573
+ from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
574
+ BasisTradingStrategy,
575
+ )
576
+
577
+ s = BasisTradingStrategy(
578
+ config={
579
+ "main_wallet": {"address": "0x1234"},
580
+ "strategy_wallet": {"address": "0x5678"},
581
+ },
582
+ )
583
+ s.hyperliquid_adapter = mock_hyperliquid_adapter
584
+ s.ledger_adapter = ledger_adapter
585
+
586
+ # Mock get_max_builder_fee returning sufficient approval
587
+ mock_hyperliquid_adapter.get_max_builder_fee = AsyncMock(
588
+ return_value=(
589
+ True,
590
+ 30,
591
+ ) # Already approved for 30 tenths bp
592
+ )
593
+ mock_hyperliquid_adapter.approve_builder_fee = AsyncMock(
594
+ return_value=(True, {"status": "ok"})
595
+ )
596
+
597
+ success, msg = await s.ensure_builder_fee_approved()
598
+ assert success
599
+ assert "already approved" in msg.lower()
600
+ # Should not have called approve_builder_fee
601
+ mock_hyperliquid_adapter.approve_builder_fee.assert_not_called()
619
602
 
620
603
  @pytest.mark.asyncio
621
604
  async def test_ensure_builder_fee_approved_needs_approval(
@@ -636,35 +619,32 @@ class TestBasisTradingStrategy:
636
619
  "wayfinder_paths.strategies.basis_trading_strategy.strategy.LedgerAdapter",
637
620
  return_value=ledger_adapter,
638
621
  ):
639
- with patch(
640
- "wayfinder_paths.strategies.basis_trading_strategy.strategy.WalletManager"
641
- ):
642
- from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
643
- BasisTradingStrategy,
644
- )
645
-
646
- s = BasisTradingStrategy(
647
- config={
648
- "main_wallet": {"address": "0x1234"},
649
- "strategy_wallet": {"address": "0x5678"},
650
- },
651
- )
652
- s.hyperliquid_adapter = mock_hyperliquid_adapter
653
- s.ledger_adapter = ledger_adapter
654
-
655
- # Mock get_max_builder_fee returning insufficient approval
656
- mock_hyperliquid_adapter.get_max_builder_fee = AsyncMock(
657
- return_value=(True, 0) # Not approved yet
658
- )
659
- mock_hyperliquid_adapter.approve_builder_fee = AsyncMock(
660
- return_value=(True, {"status": "ok"})
661
- )
662
-
663
- success, msg = await s.ensure_builder_fee_approved()
664
- assert success
665
- assert "approved" in msg.lower()
666
- # Should have called approve_builder_fee
667
- mock_hyperliquid_adapter.approve_builder_fee.assert_called_once()
622
+ from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
623
+ BasisTradingStrategy,
624
+ )
625
+
626
+ s = BasisTradingStrategy(
627
+ config={
628
+ "main_wallet": {"address": "0x1234"},
629
+ "strategy_wallet": {"address": "0x5678"},
630
+ },
631
+ )
632
+ s.hyperliquid_adapter = mock_hyperliquid_adapter
633
+ s.ledger_adapter = ledger_adapter
634
+
635
+ # Mock get_max_builder_fee returning insufficient approval
636
+ mock_hyperliquid_adapter.get_max_builder_fee = AsyncMock(
637
+ return_value=(True, 0) # Not approved yet
638
+ )
639
+ mock_hyperliquid_adapter.approve_builder_fee = AsyncMock(
640
+ return_value=(True, {"status": "ok"})
641
+ )
642
+
643
+ success, msg = await s.ensure_builder_fee_approved()
644
+ assert success
645
+ assert "approved" in msg.lower()
646
+ # Should have called approve_builder_fee
647
+ mock_hyperliquid_adapter.approve_builder_fee.assert_called_once()
668
648
 
669
649
  @pytest.mark.asyncio
670
650
  async def test_portfolio_value_includes_spot_holdings(
@@ -954,29 +934,26 @@ class TestBasisTradingStrategy:
954
934
  "wayfinder_paths.strategies.basis_trading_strategy.strategy.LedgerAdapter",
955
935
  return_value=ledger_adapter,
956
936
  ):
957
- with patch(
958
- "wayfinder_paths.strategies.basis_trading_strategy.strategy.WalletManager"
959
- ):
960
- from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
961
- BasisTradingStrategy,
962
- )
963
-
964
- s = BasisTradingStrategy(
965
- config={
966
- "main_wallet": {"address": "0x1234"},
967
- "strategy_wallet": {"address": "0x5678"},
968
- },
969
- )
970
- s.hyperliquid_adapter = mock_hyperliquid_adapter
971
- s.ledger_adapter = ledger_adapter
972
-
973
- # Mock get_strategy_net_deposit to return float (not dict)
974
- s.ledger_adapter.get_strategy_net_deposit = AsyncMock(
975
- return_value=(True, 2500.0)
976
- )
977
-
978
- # Run setup - should not raise AttributeError
979
- await s.setup()
980
-
981
- # Verify deposit_amount was set from the float
982
- assert s.deposit_amount == 2500.0
937
+ from wayfinder_paths.strategies.basis_trading_strategy.strategy import (
938
+ BasisTradingStrategy,
939
+ )
940
+
941
+ s = BasisTradingStrategy(
942
+ config={
943
+ "main_wallet": {"address": "0x1234"},
944
+ "strategy_wallet": {"address": "0x5678"},
945
+ },
946
+ )
947
+ s.hyperliquid_adapter = mock_hyperliquid_adapter
948
+ s.ledger_adapter = ledger_adapter
949
+
950
+ # Mock get_strategy_net_deposit to return float (not dict)
951
+ s.ledger_adapter.get_strategy_net_deposit = AsyncMock(
952
+ return_value=(True, 2500.0)
953
+ )
954
+
955
+ # Run setup - should not raise AttributeError
956
+ await s.setup()
957
+
958
+ # Verify deposit_amount was set from the float
959
+ assert s.deposit_amount == 2500.0
@@ -30,7 +30,6 @@ Allocates USDT0 on HyperEVM across HyperLend stablecoin markets. The strategy:
30
30
  - `LedgerAdapter` for net deposit + rotation history.
31
31
  - `BRAPAdapter` to source quotes/swap stablecoins.
32
32
  - `HyperlendAdapter` for asset views, lend/withdraw ops, supply caps.
33
- - `LocalTokenTxnService` via `DefaultWeb3Service` for low-level sends/approvals leveraged by the adapters.
34
33
 
35
34
  ## Actions
36
35
 
@@ -2,6 +2,7 @@ import asyncio
2
2
  import math
3
3
  import time
4
4
  import unicodedata
5
+ from collections.abc import Awaitable, Callable
5
6
  from datetime import UTC, datetime, timedelta, timezone
6
7
  from decimal import ROUND_DOWN, ROUND_UP, Decimal
7
8
  from typing import Any, Literal
@@ -17,11 +18,6 @@ from wayfinder_paths.adapters.hyperlend_adapter.adapter import HyperlendAdapter
17
18
  from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
18
19
  from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
19
20
  from wayfinder_paths.core.constants.base import DEFAULT_SLIPPAGE
20
- from wayfinder_paths.core.services.base import Web3Service
21
- from wayfinder_paths.core.services.local_token_txn import (
22
- LocalTokenTxnService,
23
- )
24
- from wayfinder_paths.core.services.web3_service import DefaultWeb3Service
25
21
  from wayfinder_paths.core.strategies.descriptors import (
26
22
  Complexity,
27
23
  Directionality,
@@ -31,7 +27,6 @@ from wayfinder_paths.core.strategies.descriptors import (
31
27
  Volatility,
32
28
  )
33
29
  from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
34
- from wayfinder_paths.core.wallets.WalletManager import WalletManager
35
30
  from wayfinder_paths.policies.enso import ENSO_ROUTER, enso_swap
36
31
  from wayfinder_paths.policies.erc20 import erc20_spender_for_any_token
37
32
  from wayfinder_paths.policies.hyper_evm import (
@@ -196,9 +191,16 @@ class HyperlendStableYieldStrategy(Strategy):
196
191
  *,
197
192
  main_wallet: dict[str, Any] | None = None,
198
193
  strategy_wallet: dict[str, Any] | None = None,
199
- web3_service: Web3Service = None,
194
+ api_key: str | None = None,
195
+ main_wallet_signing_callback: Callable[[dict], Awaitable[str]] | None = None,
196
+ strategy_wallet_signing_callback: Callable[[dict], Awaitable[str]]
197
+ | None = None,
200
198
  ):
201
- super().__init__()
199
+ super().__init__(
200
+ api_key=api_key,
201
+ main_wallet_signing_callback=main_wallet_signing_callback,
202
+ strategy_wallet_signing_callback=strategy_wallet_signing_callback,
203
+ )
202
204
  merged_config: dict[str, Any] = dict(config or {})
203
205
  if main_wallet is not None:
204
206
  merged_config["main_wallet"] = main_wallet
@@ -207,10 +209,7 @@ class HyperlendStableYieldStrategy(Strategy):
207
209
 
208
210
  self.config = merged_config
209
211
  self.balance_adapter = None
210
- self.tx_adapter = None
211
212
  self.token_adapter = None
212
- self.evm_transaction_adapter = None
213
- self.web3_service = web3_service
214
213
  self.pool_adapter = None
215
214
  self.brap_adapter = None
216
215
  self.hyperlend_adapter = None
@@ -231,26 +230,20 @@ class HyperlendStableYieldStrategy(Strategy):
231
230
  "strategy": self.config,
232
231
  }
233
232
 
234
- if self.web3_service is None:
235
- wallet_provider = WalletManager.get_provider(adapter_config)
236
- token_transaction_service = LocalTokenTxnService(
237
- adapter_config,
238
- wallet_provider=wallet_provider,
239
- )
240
- web3_service = DefaultWeb3Service(
241
- wallet_provider=wallet_provider,
242
- evm_transactions=token_transaction_service,
243
- )
244
- else:
245
- web3_service = self.web3_service
246
- token_transaction_service = web3_service.token_transactions
247
- balance = BalanceAdapter(adapter_config, web3_service=web3_service)
233
+ balance = BalanceAdapter(
234
+ adapter_config,
235
+ main_wallet_signing_callback=self.main_wallet_signing_callback,
236
+ strategy_wallet_signing_callback=self.strategy_wallet_signing_callback,
237
+ )
248
238
  token_adapter = TokenAdapter()
249
- ledger_adapter = LedgerAdapter() # here
250
- brap_adapter = BRAPAdapter(web3_service=web3_service)
239
+ ledger_adapter = LedgerAdapter()
240
+ brap_adapter = BRAPAdapter(
241
+ adapter_config,
242
+ strategy_wallet_signing_callback=self.strategy_wallet_signing_callback,
243
+ )
251
244
  hyperlend_adapter = HyperlendAdapter(
252
245
  adapter_config,
253
- web3_service=web3_service,
246
+ strategy_wallet_signing_callback=self.strategy_wallet_signing_callback,
254
247
  )
255
248
 
256
249
  self.register_adapters(
@@ -260,12 +253,9 @@ class HyperlendStableYieldStrategy(Strategy):
260
253
  ledger_adapter,
261
254
  brap_adapter,
262
255
  hyperlend_adapter,
263
- token_transaction_service,
264
256
  ]
265
257
  )
266
258
  self.balance_adapter = balance
267
- self.evm_transaction_adapter = token_transaction_service
268
- self.web3_service = web3_service
269
259
  self.token_adapter = token_adapter
270
260
  self.ledger_adapter = ledger_adapter
271
261
  self.brap_adapter = brap_adapter
@@ -863,76 +853,60 @@ class HyperlendStableYieldStrategy(Strategy):
863
853
  self.usdt_token_info
864
854
  )
865
855
 
856
+ # Get final balances in strategy wallet (don't transfer to main)
857
+ total_usdt = 0.0
866
858
  try:
867
859
  _, total_usdt_wei = await self.balance_adapter.get_balance(
868
860
  query=self.usdt_token_info.get("token_id"),
869
861
  wallet_address=self._get_strategy_wallet_address(),
870
862
  )
871
- except Exception:
872
- total_usdt_wei = 0
873
-
874
- if total_usdt_wei and total_usdt_wei > 0:
875
- total_usdt = float(total_usdt_wei) / (
876
- 10 ** self.usdt_token_info.get("decimals", 18)
877
- )
878
- (
879
- transfer_success,
880
- transfer_message,
881
- ) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
882
- self.usdt_token_info.get("token_id"),
883
- total_usdt,
884
- strategy_name=self.name,
885
- )
886
- if transfer_success:
887
- messages.append(
888
- f"Returned {total_usdt:.2f} {self.usdt_token_info.get('symbol')} from strategy wallet to main wallet"
889
- )
890
- else:
891
- messages.append(
892
- "Returned USDT0 to ledger but on-chain transfer failed; treating as withdrawn for simulation"
863
+ if total_usdt_wei and total_usdt_wei > 0:
864
+ total_usdt = float(total_usdt_wei) / (
865
+ 10 ** self.usdt_token_info.get("decimals", 18)
893
866
  )
867
+ except Exception:
868
+ pass
894
869
 
870
+ total_hype = 0.0
895
871
  try:
896
872
  _, total_hype_wei = await self.balance_adapter.get_balance(
897
873
  query=self.hype_token_info.get("token_id"),
898
874
  wallet_address=self._get_strategy_wallet_address(),
899
875
  )
900
- except Exception:
901
- total_hype_wei = 0
902
-
903
- if total_hype_wei and total_hype_wei > 0:
904
- total_hype = float(total_hype_wei) / (
905
- 10 ** self.hype_token_info.get("decimals", 18)
906
- )
907
- total_hype = total_hype * 0.9
908
- (
909
- transfer_success,
910
- transfer_message,
911
- ) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
912
- self.hype_token_info.get("token_id"),
913
- total_hype,
914
- strategy_name=self.name,
915
- )
916
- if transfer_success:
917
- messages.append(
918
- f"Returned {total_hype:.2f} {self.hype_token_info.get('symbol')} from strategy wallet to main wallet"
919
- )
920
- else:
921
- messages.append(
922
- "Returned HYPE to ledger but on-chain transfer failed; treating as withdrawn for simulation"
876
+ if total_hype_wei and total_hype_wei > 0:
877
+ total_hype = float(total_hype_wei) / (
878
+ 10 ** self.hype_token_info.get("decimals", 18)
923
879
  )
880
+ except Exception:
881
+ pass
924
882
 
925
883
  if sweep_actions:
926
884
  messages.append(f"Residual sweeps: {'; '.join(sweep_actions)}.")
927
885
 
928
- if not messages:
929
- messages.append("Withdrawal complete; no balances detected to transfer")
886
+ # Report balances in strategy wallet
887
+ balance_parts = []
888
+ if total_usdt > 0:
889
+ balance_parts.append(
890
+ f"{total_usdt:.2f} {self.usdt_token_info.get('symbol')}"
891
+ )
892
+ if total_hype > 0:
893
+ balance_parts.append(
894
+ f"{total_hype:.4f} {self.hype_token_info.get('symbol')}"
895
+ )
896
+
897
+ if balance_parts:
898
+ messages.append(f"Strategy wallet balance: {', '.join(balance_parts)}")
930
899
 
931
900
  self.current_token = None
932
901
  self.current_symbol = None
933
902
  self.current_avg_apy = 0.0
934
903
  self.kept_hype_tokens = 0.0
935
904
 
905
+ strategy_address = self._get_strategy_wallet_address()
906
+ messages.append(
907
+ f"Call exit() to transfer funds from strategy wallet ({strategy_address}) to main wallet"
908
+ )
909
+
936
910
  return (True, ". ".join(messages))
937
911
 
938
912
  async def exit(self, **kwargs) -> StatusTuple:
@@ -121,14 +121,6 @@ def strategy():
121
121
  "decimals": 6,
122
122
  }
123
123
  )
124
- s.balance_adapter.token_transactions = AsyncMock()
125
- s.balance_adapter.token_transactions.build_send = AsyncMock(
126
- return_value=(True, {"transaction": "0xMOCK"})
127
- )
128
- s.balance_adapter.wallet_provider = AsyncMock()
129
- s.balance_adapter.wallet_provider.broadcast_transaction = AsyncMock(
130
- return_value=(True, {"transaction_hash": "0xCAFEBABE"})
131
- )
132
124
  # token_adapter might already be set, so check before overriding
133
125
  if (
134
126
  not hasattr(s.balance_adapter, "token_adapter")
@@ -205,10 +197,6 @@ def strategy():
205
197
  and hasattr(s.brap_adapter, "swap_from_quote")
206
198
  ):
207
199
  s.brap_adapter.swap_from_quote = AsyncMock(return_value=None)
208
- if hasattr(s, "brap_adapter") and hasattr(s.brap_adapter, "wallet_provider"):
209
- s.brap_adapter.wallet_provider.broadcast_transaction = AsyncMock(
210
- return_value=(True, {"transaction_hash": "0xF00D"})
211
- )
212
200
 
213
201
  if hasattr(s, "hyperlend_adapter") and s.hyperlend_adapter:
214
202
  s.hyperlend_adapter.get_assets_view = AsyncMock(
@@ -44,7 +44,6 @@ The position is **delta-neutral**: WETH debt offsets wstETH collateral, so PnL i
44
44
  - `LedgerAdapter` for net deposit tracking.
45
45
  - `BRAPAdapter` for swap quotes and execution via Aerodrome/routing.
46
46
  - `MoonwellAdapter` for lending, borrowing, collateral management, and position queries.
47
- - `LocalTokenTxnService` via `DefaultWeb3Service` for low-level sends/approvals.
48
47
 
49
48
  ## Actions
50
49