wayfinder-paths 0.1.13__py3-none-any.whl → 0.1.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wayfinder-paths might be problematic. Click here for more details.
- wayfinder_paths/adapters/balance_adapter/README.md +13 -14
- wayfinder_paths/adapters/balance_adapter/adapter.py +33 -32
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +123 -0
- wayfinder_paths/adapters/brap_adapter/README.md +11 -16
- wayfinder_paths/adapters/brap_adapter/adapter.py +78 -63
- wayfinder_paths/adapters/brap_adapter/examples.json +63 -52
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +121 -59
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +16 -14
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +114 -60
- wayfinder_paths/adapters/pool_adapter/README.md +9 -10
- wayfinder_paths/adapters/pool_adapter/adapter.py +9 -10
- wayfinder_paths/adapters/token_adapter/README.md +2 -14
- wayfinder_paths/adapters/token_adapter/adapter.py +16 -10
- wayfinder_paths/adapters/token_adapter/examples.json +4 -8
- wayfinder_paths/adapters/token_adapter/test_adapter.py +5 -3
- wayfinder_paths/core/clients/BRAPClient.py +102 -61
- wayfinder_paths/core/clients/ClientManager.py +1 -68
- wayfinder_paths/core/clients/HyperlendClient.py +125 -64
- wayfinder_paths/core/clients/LedgerClient.py +1 -4
- wayfinder_paths/core/clients/PoolClient.py +122 -48
- wayfinder_paths/core/clients/TokenClient.py +91 -36
- wayfinder_paths/core/clients/WalletClient.py +26 -56
- wayfinder_paths/core/clients/WayfinderClient.py +28 -160
- wayfinder_paths/core/clients/__init__.py +0 -2
- wayfinder_paths/core/clients/protocols.py +35 -46
- wayfinder_paths/core/clients/sdk_example.py +37 -22
- wayfinder_paths/core/engine/StrategyJob.py +7 -55
- wayfinder_paths/core/services/local_evm_txn.py +6 -6
- wayfinder_paths/core/services/local_token_txn.py +1 -1
- wayfinder_paths/core/strategies/Strategy.py +0 -2
- wayfinder_paths/core/utils/evm_helpers.py +2 -2
- wayfinder_paths/run_strategy.py +8 -19
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +10 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +40 -25
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +54 -9
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +3 -3
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +12 -6
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +1 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +88 -56
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +16 -12
- wayfinder_paths/templates/strategy/README.md +3 -3
- wayfinder_paths/templates/strategy/test_strategy.py +3 -2
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/METADATA +14 -49
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/RECORD +46 -47
- wayfinder_paths/core/clients/AuthClient.py +0 -83
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.13.dist-info → wayfinder_paths-0.1.14.dist-info}/WHEEL +0 -0
|
@@ -154,7 +154,7 @@ class LocalTokenTxnService(TokenTxn):
|
|
|
154
154
|
|
|
155
155
|
def _chain_id(self, chain: Any) -> int:
|
|
156
156
|
if isinstance(chain, dict):
|
|
157
|
-
chain_id = chain.get("id")
|
|
157
|
+
chain_id = chain.get("id")
|
|
158
158
|
else:
|
|
159
159
|
chain_id = getattr(chain, "id", None)
|
|
160
160
|
if chain_id is None:
|
|
@@ -57,12 +57,10 @@ class Strategy(ABC):
|
|
|
57
57
|
main_wallet: WalletConfig | dict[str, Any] | None = None,
|
|
58
58
|
strategy_wallet: WalletConfig | dict[str, Any] | None = None,
|
|
59
59
|
web3_service: Web3Service | None = None,
|
|
60
|
-
api_key: str | None = None,
|
|
61
60
|
):
|
|
62
61
|
self.adapters = {}
|
|
63
62
|
self.ledger_adapter = None
|
|
64
63
|
self.logger = logger.bind(strategy=self.__class__.__name__)
|
|
65
|
-
# Note: api_key is passed to ClientManager, not set in environment
|
|
66
64
|
self.config = config
|
|
67
65
|
|
|
68
66
|
async def setup(self) -> None:
|
|
@@ -43,12 +43,12 @@ def resolve_chain_id(token_info: dict[str, Any], logger_instance=None) -> int |
|
|
|
43
43
|
"""
|
|
44
44
|
log = logger_instance or logger
|
|
45
45
|
chain_meta = token_info.get("chain") or {}
|
|
46
|
-
chain_id = chain_meta.get("
|
|
46
|
+
chain_id = chain_meta.get("id")
|
|
47
47
|
try:
|
|
48
48
|
if chain_id is not None:
|
|
49
49
|
return int(chain_id)
|
|
50
50
|
except (ValueError, TypeError):
|
|
51
|
-
log.debug("Invalid chain_id in token_info.
|
|
51
|
+
log.debug("Invalid chain_id in token_info.chain: %s", chain_id)
|
|
52
52
|
return chain_code_to_chain_id(chain_meta.get("code"))
|
|
53
53
|
|
|
54
54
|
|
wayfinder_paths/run_strategy.py
CHANGED
|
@@ -20,7 +20,6 @@ def load_strategy(
|
|
|
20
20
|
strategy_name: str,
|
|
21
21
|
*,
|
|
22
22
|
strategy_config: dict | None = None,
|
|
23
|
-
api_key: str | None = None,
|
|
24
23
|
):
|
|
25
24
|
"""
|
|
26
25
|
Dynamically load a strategy by name
|
|
@@ -28,7 +27,6 @@ def load_strategy(
|
|
|
28
27
|
Args:
|
|
29
28
|
strategy_name: Name of the strategy to load (directory name in strategies/)
|
|
30
29
|
strategy_config: Configuration dict for the strategy
|
|
31
|
-
api_key: Optional API key for service account authentication
|
|
32
30
|
|
|
33
31
|
Returns:
|
|
34
32
|
Strategy instance
|
|
@@ -70,7 +68,7 @@ def load_strategy(
|
|
|
70
68
|
if strategy_class is None:
|
|
71
69
|
raise ValueError(f"No Strategy class found in {module_path}")
|
|
72
70
|
|
|
73
|
-
return strategy_class(config=strategy_config
|
|
71
|
+
return strategy_class(config=strategy_config)
|
|
74
72
|
|
|
75
73
|
|
|
76
74
|
def load_config(
|
|
@@ -135,18 +133,15 @@ async def run_strategy(
|
|
|
135
133
|
# Load configuration with strategy name for wallet lookup
|
|
136
134
|
logger.debug(f"Config path provided: {config_path}")
|
|
137
135
|
config = load_config(config_path, strategy_name=strategy_name)
|
|
138
|
-
creds = (
|
|
139
|
-
"yes"
|
|
140
|
-
if (config.user.username and config.user.password)
|
|
141
|
-
or config.user.refresh_token
|
|
142
|
-
else "no"
|
|
143
|
-
)
|
|
144
|
-
main_wallet = config.user.main_wallet_address or "none"
|
|
145
|
-
strategy_wallet = config.user.strategy_wallet_address or "none"
|
|
146
136
|
logger.debug(
|
|
147
|
-
|
|
137
|
+
"Loaded config: wallets(main={} strategy={})",
|
|
138
|
+
config.user.main_wallet_address or "none",
|
|
139
|
+
config.user.strategy_wallet_address or "none",
|
|
148
140
|
)
|
|
149
141
|
|
|
142
|
+
# Validate required configuration
|
|
143
|
+
# Authentication is via system.api_key in config.json
|
|
144
|
+
|
|
150
145
|
# Load strategy with the enriched config
|
|
151
146
|
strategy = load_strategy(
|
|
152
147
|
strategy_name,
|
|
@@ -159,13 +154,7 @@ async def run_strategy(
|
|
|
159
154
|
|
|
160
155
|
# Setup strategy job
|
|
161
156
|
logger.info("Setting up strategy job...")
|
|
162
|
-
|
|
163
|
-
"credentials"
|
|
164
|
-
if (config.user.username and config.user.password)
|
|
165
|
-
or config.user.refresh_token
|
|
166
|
-
else "missing"
|
|
167
|
-
)
|
|
168
|
-
logger.debug(f"Auth mode: {auth_mode}")
|
|
157
|
+
logger.debug("Auth mode: API key (from system.api_key)")
|
|
169
158
|
await strategy_job.setup()
|
|
170
159
|
|
|
171
160
|
# Execute action
|
|
@@ -203,9 +203,8 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
203
203
|
strategy_wallet: dict[str, Any] | None = None,
|
|
204
204
|
web3_service: Web3Service | None = None,
|
|
205
205
|
hyperliquid_executor: HyperliquidExecutor | None = None,
|
|
206
|
-
api_key: str | None = None,
|
|
207
206
|
) -> None:
|
|
208
|
-
super().__init__(
|
|
207
|
+
super().__init__()
|
|
209
208
|
|
|
210
209
|
merged_config = dict(config or {})
|
|
211
210
|
if main_wallet:
|
|
@@ -479,7 +478,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
479
478
|
gas_ok,
|
|
480
479
|
gas_res,
|
|
481
480
|
) = await self.balance_adapter.move_from_main_wallet_to_strategy_wallet(
|
|
482
|
-
|
|
481
|
+
query="ethereum-arbitrum", # Native ETH on Arbitrum
|
|
483
482
|
amount=gas_token_amount,
|
|
484
483
|
strategy_name=self.name or "basis_trading_strategy",
|
|
485
484
|
skip_ledger=True,
|
|
@@ -500,7 +499,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
500
499
|
strategy_balance_ok,
|
|
501
500
|
strategy_balance,
|
|
502
501
|
) = await self.balance_adapter.get_balance(
|
|
503
|
-
|
|
502
|
+
query=USDC_ARBITRUM_TOKEN_ID,
|
|
504
503
|
wallet_address=strategy_address,
|
|
505
504
|
)
|
|
506
505
|
strategy_usdc = 0.0
|
|
@@ -518,7 +517,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
518
517
|
move_ok,
|
|
519
518
|
move_res,
|
|
520
519
|
) = await self.balance_adapter.move_from_main_wallet_to_strategy_wallet(
|
|
521
|
-
|
|
520
|
+
query=USDC_ARBITRUM_TOKEN_ID,
|
|
522
521
|
amount=need_to_move,
|
|
523
522
|
strategy_name=self.name or "basis_trading_strategy",
|
|
524
523
|
skip_ledger=True,
|
|
@@ -543,7 +542,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
543
542
|
|
|
544
543
|
# Send USDC to bridge address (deposit credits the sender address on Hyperliquid)
|
|
545
544
|
success, result = await self.balance_adapter.send_to_address(
|
|
546
|
-
|
|
545
|
+
query=USDC_ARBITRUM_TOKEN_ID,
|
|
547
546
|
amount=main_token_amount,
|
|
548
547
|
from_wallet=strategy_wallet,
|
|
549
548
|
to_address=HYPERLIQUID_BRIDGE_ADDRESS,
|
|
@@ -821,7 +820,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
821
820
|
strategy_usdc = 0.0
|
|
822
821
|
try:
|
|
823
822
|
success, balance_data = await self.balance_adapter.get_balance(
|
|
824
|
-
|
|
823
|
+
query=usdc_token_id,
|
|
825
824
|
wallet_address=address,
|
|
826
825
|
)
|
|
827
826
|
if success:
|
|
@@ -866,7 +865,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
866
865
|
send_success,
|
|
867
866
|
send_result,
|
|
868
867
|
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
869
|
-
|
|
868
|
+
query=usdc_token_id,
|
|
870
869
|
amount=amount_to_send,
|
|
871
870
|
strategy_name=self.name,
|
|
872
871
|
skip_ledger=False,
|
|
@@ -1011,7 +1010,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
1011
1010
|
final_balance = 0.0
|
|
1012
1011
|
try:
|
|
1013
1012
|
success, balance_data = await self.balance_adapter.get_balance(
|
|
1014
|
-
|
|
1013
|
+
query=usdc_token_id,
|
|
1015
1014
|
wallet_address=address,
|
|
1016
1015
|
)
|
|
1017
1016
|
if success:
|
|
@@ -1032,7 +1031,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
1032
1031
|
send_success,
|
|
1033
1032
|
send_result,
|
|
1034
1033
|
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
1035
|
-
|
|
1034
|
+
query=usdc_token_id,
|
|
1036
1035
|
amount=amount_to_send,
|
|
1037
1036
|
strategy_name=self.name,
|
|
1038
1037
|
skip_ledger=False, # Record in ledger
|
|
@@ -2569,7 +2568,7 @@ class BasisTradingStrategy(BasisSnapshotMixin, Strategy):
|
|
|
2569
2568
|
try:
|
|
2570
2569
|
strategy_address = self._get_strategy_wallet_address()
|
|
2571
2570
|
success, balance = await self.balance_adapter.get_balance(
|
|
2572
|
-
|
|
2571
|
+
query=USDC_ARBITRUM_TOKEN_ID,
|
|
2573
2572
|
wallet_address=strategy_address,
|
|
2574
2573
|
)
|
|
2575
2574
|
if success and balance:
|
|
@@ -197,9 +197,8 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
197
197
|
main_wallet: dict[str, Any] | None = None,
|
|
198
198
|
strategy_wallet: dict[str, Any] | None = None,
|
|
199
199
|
web3_service: Web3Service = None,
|
|
200
|
-
api_key: str | None = None,
|
|
201
200
|
):
|
|
202
|
-
super().__init__(
|
|
201
|
+
super().__init__()
|
|
203
202
|
merged_config: dict[str, Any] = dict(config or {})
|
|
204
203
|
if main_wallet is not None:
|
|
205
204
|
merged_config["main_wallet"] = main_wallet
|
|
@@ -350,7 +349,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
350
349
|
success,
|
|
351
350
|
main_usdt0_balance,
|
|
352
351
|
) = await self.balance_adapter.get_balance(
|
|
353
|
-
|
|
352
|
+
query=self.usdt_token_info.get("token_id"),
|
|
354
353
|
wallet_address=self._get_main_wallet_address(),
|
|
355
354
|
)
|
|
356
355
|
if not success:
|
|
@@ -363,7 +362,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
363
362
|
success,
|
|
364
363
|
main_hype_balance,
|
|
365
364
|
) = await self.balance_adapter.get_balance(
|
|
366
|
-
|
|
365
|
+
query=self.hype_token_info.get("token_id"),
|
|
367
366
|
wallet_address=self._get_main_wallet_address(),
|
|
368
367
|
)
|
|
369
368
|
if not success:
|
|
@@ -530,11 +529,10 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
530
529
|
return self._assets_snapshot
|
|
531
530
|
|
|
532
531
|
_, snapshot = await self.hyperlend_adapter.get_assets_view(
|
|
533
|
-
chain_id=self.hype_token_info.get("chain").get("id"),
|
|
534
532
|
user_address=self._get_strategy_wallet_address(),
|
|
535
533
|
)
|
|
536
534
|
|
|
537
|
-
assets = snapshot.get("
|
|
535
|
+
assets = snapshot.get("assets", [])
|
|
538
536
|
asset_map = {}
|
|
539
537
|
|
|
540
538
|
for asset in assets:
|
|
@@ -582,11 +580,9 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
582
580
|
|
|
583
581
|
try:
|
|
584
582
|
_, data = await self.hyperlend_adapter.get_stable_markets(
|
|
585
|
-
chain_id=self.hype_token_info.get("chain").get("id"),
|
|
586
583
|
required_underlying_tokens=required_tokens,
|
|
587
584
|
buffer_bps=self.SUPPLY_CAP_BUFFER_BPS,
|
|
588
585
|
min_buffer_tokens=self.SUPPLY_CAP_MIN_BUFFER_TOKENS,
|
|
589
|
-
is_stable_symbol=True,
|
|
590
586
|
)
|
|
591
587
|
markets = data.get("markets", {}) if isinstance(data, dict) else {}
|
|
592
588
|
except Exception:
|
|
@@ -609,7 +605,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
609
605
|
async def _get_lent_positions(self, snapshot=None) -> dict[str, dict[str, Any]]:
|
|
610
606
|
if not snapshot:
|
|
611
607
|
snapshot = await self._get_assets_snapshot()
|
|
612
|
-
assets = snapshot.get("
|
|
608
|
+
assets = snapshot.get("assets", None)
|
|
613
609
|
|
|
614
610
|
if not assets:
|
|
615
611
|
return {}
|
|
@@ -630,7 +626,14 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
630
626
|
continue
|
|
631
627
|
|
|
632
628
|
try:
|
|
633
|
-
|
|
629
|
+
chain_id = None
|
|
630
|
+
try:
|
|
631
|
+
chain_id = int((self.hype_token_info.get("chain") or {}).get("id"))
|
|
632
|
+
except Exception:
|
|
633
|
+
chain_id = None
|
|
634
|
+
success, token = await self.token_adapter.get_token(
|
|
635
|
+
checksum, chain_id=chain_id
|
|
636
|
+
)
|
|
634
637
|
if not success or not isinstance(token, dict):
|
|
635
638
|
logger.info(f"Error getting token for asset: {asset}")
|
|
636
639
|
continue
|
|
@@ -716,8 +719,8 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
716
719
|
result,
|
|
717
720
|
tx_data,
|
|
718
721
|
) = await self.brap_adapter.swap_from_token_ids(
|
|
719
|
-
|
|
720
|
-
|
|
722
|
+
from_query=from_token_id,
|
|
723
|
+
to_query=to_token_id,
|
|
721
724
|
from_address=strategy_address,
|
|
722
725
|
amount=amount_wei_str,
|
|
723
726
|
slippage=slippage,
|
|
@@ -862,7 +865,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
862
865
|
|
|
863
866
|
try:
|
|
864
867
|
_, total_usdt_wei = await self.balance_adapter.get_balance(
|
|
865
|
-
|
|
868
|
+
query=self.usdt_token_info.get("token_id"),
|
|
866
869
|
wallet_address=self._get_strategy_wallet_address(),
|
|
867
870
|
)
|
|
868
871
|
except Exception:
|
|
@@ -891,7 +894,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
891
894
|
|
|
892
895
|
try:
|
|
893
896
|
_, total_hype_wei = await self.balance_adapter.get_balance(
|
|
894
|
-
|
|
897
|
+
query=self.hype_token_info.get("token_id"),
|
|
895
898
|
wallet_address=self._get_strategy_wallet_address(),
|
|
896
899
|
)
|
|
897
900
|
except Exception:
|
|
@@ -987,7 +990,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
987
990
|
success,
|
|
988
991
|
message,
|
|
989
992
|
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
990
|
-
|
|
993
|
+
query=token_info.get("token_id"),
|
|
991
994
|
amount=amount_tokens,
|
|
992
995
|
strategy_name=self.name,
|
|
993
996
|
)
|
|
@@ -1549,11 +1552,9 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1549
1552
|
)
|
|
1550
1553
|
|
|
1551
1554
|
_, stable_markets = await self.hyperlend_adapter.get_stable_markets(
|
|
1552
|
-
chain_id=self.hype_token_info.get("chain").get("id"),
|
|
1553
1555
|
required_underlying_tokens=required_underlying_tokens,
|
|
1554
1556
|
buffer_bps=self.SUPPLY_CAP_BUFFER_BPS,
|
|
1555
1557
|
min_buffer_tokens=self.SUPPLY_CAP_MIN_BUFFER_TOKENS,
|
|
1556
|
-
is_stable_symbol=True,
|
|
1557
1558
|
)
|
|
1558
1559
|
filtered_notes = stable_markets.get("notes", [])
|
|
1559
1560
|
filtered_map = stable_markets.get("markets", {})
|
|
@@ -1585,8 +1586,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1585
1586
|
if current_checksum_lower not in existing_addresses:
|
|
1586
1587
|
try:
|
|
1587
1588
|
_, current_entry = await self.hyperlend_adapter.get_market_entry(
|
|
1588
|
-
|
|
1589
|
-
token_address=current_checksum_value,
|
|
1589
|
+
token=current_checksum_value,
|
|
1590
1590
|
)
|
|
1591
1591
|
except Exception:
|
|
1592
1592
|
current_entry = None
|
|
@@ -1633,8 +1633,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1633
1633
|
histories = await asyncio.gather(
|
|
1634
1634
|
*[
|
|
1635
1635
|
self.hyperlend_adapter.get_lend_rate_history(
|
|
1636
|
-
|
|
1637
|
-
token_address=addr,
|
|
1636
|
+
token=addr,
|
|
1638
1637
|
lookback_hours=lookback_hours,
|
|
1639
1638
|
)
|
|
1640
1639
|
for addr, _ in filtered
|
|
@@ -1656,7 +1655,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1656
1655
|
if not history_status:
|
|
1657
1656
|
continue
|
|
1658
1657
|
history_data = history[1]
|
|
1659
|
-
for row in history_data.get("
|
|
1658
|
+
for row in history_data.get("history", []):
|
|
1660
1659
|
ts_ms = row.get("timestamp_ms")
|
|
1661
1660
|
if ts_ms is None:
|
|
1662
1661
|
continue
|
|
@@ -1812,7 +1811,14 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1812
1811
|
token = None
|
|
1813
1812
|
if address:
|
|
1814
1813
|
try:
|
|
1815
|
-
|
|
1814
|
+
chain_id = None
|
|
1815
|
+
try:
|
|
1816
|
+
chain_id = int((self.hype_token_info.get("chain") or {}).get("id"))
|
|
1817
|
+
except Exception:
|
|
1818
|
+
chain_id = None
|
|
1819
|
+
success, token = await self.token_adapter.get_token(
|
|
1820
|
+
address.lower(), chain_id=chain_id
|
|
1821
|
+
)
|
|
1816
1822
|
except Exception:
|
|
1817
1823
|
token = None
|
|
1818
1824
|
if not success:
|
|
@@ -2128,7 +2134,16 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
2128
2134
|
continue
|
|
2129
2135
|
|
|
2130
2136
|
try:
|
|
2131
|
-
|
|
2137
|
+
chain_id = None
|
|
2138
|
+
try:
|
|
2139
|
+
chain_id = int(
|
|
2140
|
+
(self.hype_token_info.get("chain") or {}).get("id")
|
|
2141
|
+
)
|
|
2142
|
+
except Exception:
|
|
2143
|
+
chain_id = None
|
|
2144
|
+
success, token = await self.token_adapter.get_token(
|
|
2145
|
+
checksum, chain_id=chain_id
|
|
2146
|
+
)
|
|
2132
2147
|
if not success or not isinstance(token, dict):
|
|
2133
2148
|
continue
|
|
2134
2149
|
except Exception:
|
|
@@ -2239,7 +2254,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
2239
2254
|
success,
|
|
2240
2255
|
strategy_hype_balance_wei,
|
|
2241
2256
|
) = await self.balance_adapter.get_balance(
|
|
2242
|
-
|
|
2257
|
+
query=self.hype_token_info.get("token_id"),
|
|
2243
2258
|
wallet_address=self._get_strategy_wallet_address(),
|
|
2244
2259
|
)
|
|
2245
2260
|
hype_price = asset_map.get(WRAPPED_HYPE_ADDRESS, {}).get("price_usd") or 0.0
|
|
@@ -50,7 +50,10 @@ def strategy():
|
|
|
50
50
|
|
|
51
51
|
if hasattr(s, "balance_adapter") and s.balance_adapter:
|
|
52
52
|
# Mock balances: 1000 USDT0 (with 6 decimals) and 2 HYPE (with 18 decimals)
|
|
53
|
-
def get_balance_side_effect(
|
|
53
|
+
def get_balance_side_effect(query, wallet_address, **kwargs):
|
|
54
|
+
token_id = (
|
|
55
|
+
query if isinstance(query, str) else (query or {}).get("token_id")
|
|
56
|
+
)
|
|
54
57
|
token_id_str = str(token_id).lower() if token_id else ""
|
|
55
58
|
if "usdt0" in token_id_str or token_id_str == "usdt0":
|
|
56
59
|
# 1000 USDT0 with 6 decimals = 1000 * 10^6 = 1000000000
|
|
@@ -209,7 +212,31 @@ def strategy():
|
|
|
209
212
|
|
|
210
213
|
if hasattr(s, "hyperlend_adapter") and s.hyperlend_adapter:
|
|
211
214
|
s.hyperlend_adapter.get_assets_view = AsyncMock(
|
|
212
|
-
return_value=(
|
|
215
|
+
return_value=(
|
|
216
|
+
True,
|
|
217
|
+
{
|
|
218
|
+
"block_number": 12345,
|
|
219
|
+
"user": "0x0",
|
|
220
|
+
"native_balance_wei": 0,
|
|
221
|
+
"native_balance": 0.0,
|
|
222
|
+
"assets": [],
|
|
223
|
+
"account_data": {
|
|
224
|
+
"total_collateral_base": 0,
|
|
225
|
+
"total_debt_base": 0,
|
|
226
|
+
"available_borrows_base": 0,
|
|
227
|
+
"current_liquidation_threshold": 0,
|
|
228
|
+
"ltv": 0,
|
|
229
|
+
"health_factor_wad": 0,
|
|
230
|
+
"health_factor": 0.0,
|
|
231
|
+
},
|
|
232
|
+
"base_currency_info": {
|
|
233
|
+
"marketReferenceCurrencyUnit": 100000000,
|
|
234
|
+
"marketReferenceCurrencyPriceInUsd": 100000000,
|
|
235
|
+
"networkBaseTokenPriceInUsd": 0,
|
|
236
|
+
"networkBaseTokenPriceDecimals": 8,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
)
|
|
213
240
|
)
|
|
214
241
|
s.hyperlend_adapter.get_stable_markets = AsyncMock(
|
|
215
242
|
return_value=(
|
|
@@ -232,14 +259,24 @@ def strategy():
|
|
|
232
259
|
},
|
|
233
260
|
)
|
|
234
261
|
)
|
|
262
|
+
# Block bootstrap needs at least BLOCK_LEN (6) rows; provide enough history
|
|
263
|
+
_history_base = {
|
|
264
|
+
"timestamp_ms": 1700000000000,
|
|
265
|
+
"timestamp": 1700000000.0,
|
|
266
|
+
"supply_apr": 0.05,
|
|
267
|
+
"supply_apy": 0.05,
|
|
268
|
+
"borrow_apr": 0.07,
|
|
269
|
+
"borrow_apy": 0.07,
|
|
270
|
+
"token": "0x1234567890123456789012345678901234567890",
|
|
271
|
+
"symbol": "usdt0",
|
|
272
|
+
"display_symbol": "USDT0",
|
|
273
|
+
}
|
|
274
|
+
history_rows = [
|
|
275
|
+
{**_history_base, "timestamp_ms": 1700000000000 + i * 3600000}
|
|
276
|
+
for i in range(24)
|
|
277
|
+
]
|
|
235
278
|
s.hyperlend_adapter.get_lend_rate_history = AsyncMock(
|
|
236
|
-
return_value=(
|
|
237
|
-
True,
|
|
238
|
-
{
|
|
239
|
-
"rates": [{"rate": 5.0, "timestamp": 1700000000}],
|
|
240
|
-
"avg_rate": 5.0,
|
|
241
|
-
},
|
|
242
|
-
)
|
|
279
|
+
return_value=(True, {"history": history_rows})
|
|
243
280
|
)
|
|
244
281
|
|
|
245
282
|
s.usdt_token_info = {
|
|
@@ -259,6 +296,14 @@ def strategy():
|
|
|
259
296
|
"chain": {"code": "hyperevm", "id": 9999, "name": "HyperEVM"},
|
|
260
297
|
}
|
|
261
298
|
s.current_token = None
|
|
299
|
+
# Attributes normally set in setup()
|
|
300
|
+
s.rotation_policy = "hysteresis"
|
|
301
|
+
s.hys_dwell_hours = 168
|
|
302
|
+
s.hys_z = 1.15
|
|
303
|
+
s.rotation_tx_cost = 0.002
|
|
304
|
+
s.last_summary = None
|
|
305
|
+
s.last_dominance = None
|
|
306
|
+
s.last_samples = None
|
|
262
307
|
|
|
263
308
|
if hasattr(s, "token_adapter") and s.token_adapter:
|
|
264
309
|
if not hasattr(s.token_adapter, "get_token_price"):
|
|
@@ -508,7 +508,7 @@ class MoonwellWstethLoopStrategy(Strategy):
|
|
|
508
508
|
if self.balance_adapter is None:
|
|
509
509
|
return 0
|
|
510
510
|
success, raw = await self.balance_adapter.get_balance(
|
|
511
|
-
|
|
511
|
+
query=token_id,
|
|
512
512
|
wallet_address=wallet_address,
|
|
513
513
|
)
|
|
514
514
|
return self._parse_balance(raw) if success else 0
|
|
@@ -535,7 +535,7 @@ class MoonwellWstethLoopStrategy(Strategy):
|
|
|
535
535
|
|
|
536
536
|
try:
|
|
537
537
|
ok, bal = await self.balance_adapter.get_balance(
|
|
538
|
-
|
|
538
|
+
query=token_id,
|
|
539
539
|
wallet_address=wallet_address,
|
|
540
540
|
)
|
|
541
541
|
return int(bal) if ok else 0
|
|
@@ -1719,7 +1719,7 @@ class MoonwellWstethLoopStrategy(Strategy):
|
|
|
1719
1719
|
|
|
1720
1720
|
# Get actual wstETH balance
|
|
1721
1721
|
wsteth_success, wsteth_bal_raw = await self.balance_adapter.get_balance(
|
|
1722
|
-
|
|
1722
|
+
query=WSTETH_TOKEN_ID, wallet_address=self._get_strategy_wallet_address()
|
|
1723
1723
|
)
|
|
1724
1724
|
if not wsteth_success:
|
|
1725
1725
|
raise Exception("Failed to get wstETH balance after swap")
|
|
@@ -523,7 +523,8 @@ async def test_atomic_deposit_iteration_swaps_from_eth_when_borrow_surfaces_as_e
|
|
|
523
523
|
WSTETH_TOKEN_ID: 0,
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
-
async def get_balance_side_effect(*,
|
|
526
|
+
async def get_balance_side_effect(*, query: str, wallet_address: str, **_):
|
|
527
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
527
528
|
return (True, balances.get(token_id, 0))
|
|
528
529
|
|
|
529
530
|
strategy.balance_adapter.get_balance = AsyncMock(
|
|
@@ -587,7 +588,8 @@ async def test_complete_unpaired_weth_borrow_uses_eth_inventory(
|
|
|
587
588
|
WSTETH_TOKEN_ID: 0,
|
|
588
589
|
}
|
|
589
590
|
|
|
590
|
-
async def get_balance_side_effect(*,
|
|
591
|
+
async def get_balance_side_effect(*, query: str, wallet_address: str):
|
|
592
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
591
593
|
return (True, balances.get(token_id, 0))
|
|
592
594
|
|
|
593
595
|
strategy.balance_adapter.get_balance = AsyncMock(
|
|
@@ -631,8 +633,10 @@ async def test_sweep_token_balances_sweeps_tokens(strategy, mock_adapter_respons
|
|
|
631
633
|
strategy.min_withdraw_usd = 1.0
|
|
632
634
|
|
|
633
635
|
# Mock balance returns (has some WETH dust)
|
|
634
|
-
def balance_side_effect(
|
|
635
|
-
if
|
|
636
|
+
def balance_side_effect(*, query, wallet_address, **_):
|
|
637
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
638
|
+
token_id_str = (token_id or "").lower()
|
|
639
|
+
if "weth" in token_id_str:
|
|
636
640
|
return (True, 100 * 10**18) # 100 WETH
|
|
637
641
|
return (True, 0)
|
|
638
642
|
|
|
@@ -751,7 +755,8 @@ async def test_partial_liquidate_prefers_wsteth_when_excess(strategy):
|
|
|
751
755
|
# Wallet balances (raw)
|
|
752
756
|
balances: dict[str, int] = {USDC_TOKEN_ID: 0, WSTETH_TOKEN_ID: 0}
|
|
753
757
|
|
|
754
|
-
async def mock_get_balance(*,
|
|
758
|
+
async def mock_get_balance(*, query: str, wallet_address: str):
|
|
759
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
755
760
|
return (True, balances.get(token_id, 0))
|
|
756
761
|
|
|
757
762
|
strategy.balance_adapter.get_balance = AsyncMock(side_effect=mock_get_balance)
|
|
@@ -839,7 +844,8 @@ async def test_partial_liquidate_uses_usdc_collateral_when_no_wsteth_excess(stra
|
|
|
839
844
|
|
|
840
845
|
balances: dict[str, int] = {USDC_TOKEN_ID: 0}
|
|
841
846
|
|
|
842
|
-
async def mock_get_balance(*,
|
|
847
|
+
async def mock_get_balance(*, query: str, wallet_address: str):
|
|
848
|
+
token_id = query if isinstance(query, str) else (query or {}).get("token_id")
|
|
843
849
|
return (True, balances.get(token_id, 0))
|
|
844
850
|
|
|
845
851
|
strategy.balance_adapter.get_balance = AsyncMock(side_effect=mock_get_balance)
|
|
@@ -62,7 +62,7 @@ Transactions are scoped to the strategy wallet and Enso Router approval/swap cal
|
|
|
62
62
|
### Withdraw
|
|
63
63
|
|
|
64
64
|
- Requires a prior deposit (the strategy tracks `self.DEPOSIT_USDC`).
|
|
65
|
-
- Reads the pool balance via `BalanceAdapter.
|
|
65
|
+
- Reads the pool balance via `BalanceAdapter.get_balance` (with pool address and chain_id), unwinds via BRAP swaps back to USDC, and moves USDC from the strategy wallet to the main wallet via `BalanceAdapter.move_from_strategy_wallet_to_main_wallet`.
|
|
66
66
|
- Updates the ledger and clears cached pool state.
|
|
67
67
|
|
|
68
68
|
## Running locally
|