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.
- wayfinder_paths/adapters/balance_adapter/README.md +19 -20
- wayfinder_paths/adapters/balance_adapter/adapter.py +66 -37
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +2 -8
- wayfinder_paths/adapters/brap_adapter/README.md +22 -19
- wayfinder_paths/adapters/brap_adapter/adapter.py +33 -34
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +2 -18
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +40 -56
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +1 -8
- wayfinder_paths/adapters/moonwell_adapter/README.md +29 -31
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +301 -662
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +275 -179
- wayfinder_paths/core/config.py +8 -47
- wayfinder_paths/core/constants/base.py +0 -1
- wayfinder_paths/core/constants/erc20_abi.py +13 -13
- wayfinder_paths/core/strategies/Strategy.py +6 -2
- wayfinder_paths/core/utils/erc20_service.py +100 -0
- wayfinder_paths/core/utils/evm_helpers.py +1 -1
- wayfinder_paths/core/utils/transaction.py +191 -0
- wayfinder_paths/core/utils/web3.py +66 -0
- wayfinder_paths/run_strategy.py +37 -6
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +200 -224
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +128 -151
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -1
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +52 -78
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +0 -12
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +0 -1
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +39 -64
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +42 -85
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -8
- wayfinder_paths/templates/strategy/README.md +1 -5
- {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/METADATA +3 -41
- {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/RECORD +35 -44
- {wayfinder_paths-0.1.15.dist-info → wayfinder_paths-0.1.16.dist-info}/WHEEL +1 -1
- wayfinder_paths/core/clients/sdk_example.py +0 -125
- wayfinder_paths/core/engine/__init__.py +0 -5
- wayfinder_paths/core/services/__init__.py +0 -0
- wayfinder_paths/core/services/base.py +0 -131
- wayfinder_paths/core/services/local_evm_txn.py +0 -350
- wayfinder_paths/core/services/local_token_txn.py +0 -238
- wayfinder_paths/core/services/web3_service.py +0 -43
- wayfinder_paths/core/wallets/README.md +0 -88
- wayfinder_paths/core/wallets/WalletManager.py +0 -56
- wayfinder_paths/core/wallets/__init__.py +0 -7
- wayfinder_paths/scripts/run_strategy.py +0 -152
- wayfinder_paths/strategies/config.py +0 -85
- {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
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
)
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
-
|
|
640
|
-
|
|
641
|
-
)
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
-
|
|
958
|
-
|
|
959
|
-
)
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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()
|
|
250
|
-
brap_adapter = BRAPAdapter(
|
|
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
|
-
|
|
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
|
-
|
|
872
|
-
|
|
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
|
-
|
|
901
|
-
|
|
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
|
-
|
|
929
|
-
|
|
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
|
|