wayfinder-paths 0.1.4__py3-none-any.whl → 0.1.6__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/CONFIG_GUIDE.md +14 -14
- wayfinder_paths/__init__.py +4 -3
- wayfinder_paths/adapters/balance_adapter/README.md +10 -10
- wayfinder_paths/adapters/balance_adapter/adapter.py +10 -9
- wayfinder_paths/adapters/balance_adapter/examples.json +1 -1
- wayfinder_paths/adapters/brap_adapter/README.md +1 -1
- wayfinder_paths/adapters/brap_adapter/adapter.py +28 -21
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +33 -26
- wayfinder_paths/adapters/ledger_adapter/README.md +26 -39
- wayfinder_paths/adapters/ledger_adapter/adapter.py +78 -75
- wayfinder_paths/adapters/ledger_adapter/examples.json +10 -4
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +4 -4
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +31 -26
- wayfinder_paths/adapters/pool_adapter/README.md +1 -13
- wayfinder_paths/adapters/pool_adapter/adapter.py +12 -19
- wayfinder_paths/adapters/token_adapter/adapter.py +8 -4
- wayfinder_paths/core/__init__.py +9 -4
- wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
- wayfinder_paths/core/adapters/models.py +41 -0
- wayfinder_paths/core/clients/BRAPClient.py +21 -2
- wayfinder_paths/core/clients/ClientManager.py +42 -63
- wayfinder_paths/core/clients/HyperlendClient.py +46 -5
- wayfinder_paths/core/clients/LedgerClient.py +350 -124
- wayfinder_paths/core/clients/PoolClient.py +51 -19
- wayfinder_paths/core/clients/SimulationClient.py +16 -4
- wayfinder_paths/core/clients/TokenClient.py +34 -18
- wayfinder_paths/core/clients/TransactionClient.py +18 -2
- wayfinder_paths/core/clients/WalletClient.py +35 -4
- wayfinder_paths/core/clients/WayfinderClient.py +16 -5
- wayfinder_paths/core/clients/protocols.py +69 -62
- wayfinder_paths/core/clients/sdk_example.py +0 -5
- wayfinder_paths/core/config.py +192 -103
- wayfinder_paths/core/constants/base.py +17 -0
- wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
- wayfinder_paths/core/engine/__init__.py +2 -2
- wayfinder_paths/core/services/base.py +6 -4
- wayfinder_paths/core/services/local_evm_txn.py +3 -2
- wayfinder_paths/core/settings.py +2 -2
- wayfinder_paths/core/strategies/Strategy.py +135 -38
- wayfinder_paths/core/strategies/descriptors.py +1 -0
- wayfinder_paths/core/utils/evm_helpers.py +12 -10
- wayfinder_paths/core/wallets/README.md +3 -3
- wayfinder_paths/core/wallets/WalletManager.py +3 -3
- wayfinder_paths/run_strategy.py +26 -24
- wayfinder_paths/scripts/make_wallets.py +6 -6
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +6 -6
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +36 -156
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +6 -6
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +11 -11
- wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +1 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +92 -92
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +6 -6
- wayfinder_paths/templates/adapter/README.md +1 -1
- wayfinder_paths/templates/adapter/test_adapter.py +1 -1
- wayfinder_paths/templates/strategy/README.md +4 -4
- wayfinder_paths/templates/strategy/test_strategy.py +7 -7
- wayfinder_paths/tests/test_test_coverage.py +5 -5
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/METADATA +46 -47
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/RECORD +61 -60
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.6.dist-info}/WHEEL +0 -0
|
@@ -95,7 +95,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
95
95
|
**Chains:** HyperEVM only (HyperLend pool suite).
|
|
96
96
|
**Deposit/Withdrawal:** Deposits move USDT0 from the main wallet into the strategy wallet, top up a minimal HYPE gas buffer, rotate into the selected stable, and lend it via HyperLend.
|
|
97
97
|
Withdrawals unwind the lend position, convert balances back to USDT, and return funds (plus residual HYPE) to the main wallet.
|
|
98
|
-
**Gas**: Requires HYPE on HypeEVM. Arbitrary amount of funding gas is accepted via
|
|
98
|
+
**Gas**: Requires HYPE on HypeEVM. Arbitrary amount of funding gas is accepted via strategy wallet transfers.
|
|
99
99
|
""",
|
|
100
100
|
summary=(
|
|
101
101
|
"Recency-weighted HyperLend stablecoin optimizer that bootstraps 7-day rate history "
|
|
@@ -132,7 +132,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
132
132
|
return_drivers=["lend APY", "pool yield"],
|
|
133
133
|
config={
|
|
134
134
|
"deposit": {
|
|
135
|
-
"description": "Move USDT0 into the
|
|
135
|
+
"description": "Move USDT0 into the strategy, ensure a small HYPE gas buffer, and supply the best HyperLend stable.",
|
|
136
136
|
"parameters": {
|
|
137
137
|
"main_token_amount": {
|
|
138
138
|
"type": "float",
|
|
@@ -144,7 +144,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
144
144
|
"gas_token_amount": {
|
|
145
145
|
"type": "float",
|
|
146
146
|
"unit": "HYPE tokens",
|
|
147
|
-
"description": "Amount of HYPE to top up into the
|
|
147
|
+
"description": "Amount of HYPE to top up into the strategy wallet to cover gas costs.",
|
|
148
148
|
"minimum": 0.0,
|
|
149
149
|
"maximum": GAS_MAXIMUM,
|
|
150
150
|
"recommended": GAS_MAXIMUM,
|
|
@@ -176,7 +176,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
176
176
|
],
|
|
177
177
|
},
|
|
178
178
|
"points": {
|
|
179
|
-
"description": "Fetch the HyperLend points account snapshot for this
|
|
179
|
+
"description": "Fetch the HyperLend points account snapshot for this strategy wallet using a signed login.",
|
|
180
180
|
"parameters": {},
|
|
181
181
|
"result": "Returns the HyperLend points API payload for the strategy wallet.",
|
|
182
182
|
},
|
|
@@ -194,7 +194,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
194
194
|
config: dict[str, Any] | None = None,
|
|
195
195
|
*,
|
|
196
196
|
main_wallet: dict[str, Any] | None = None,
|
|
197
|
-
|
|
197
|
+
strategy_wallet: dict[str, Any] | None = None,
|
|
198
198
|
simulation: bool = False,
|
|
199
199
|
web3_service: Web3Service = None,
|
|
200
200
|
api_key: str | None = None,
|
|
@@ -203,15 +203,14 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
203
203
|
merged_config: dict[str, Any] = dict(config or {})
|
|
204
204
|
if main_wallet is not None:
|
|
205
205
|
merged_config["main_wallet"] = main_wallet
|
|
206
|
-
if
|
|
207
|
-
merged_config["
|
|
206
|
+
if strategy_wallet is not None:
|
|
207
|
+
merged_config["strategy_wallet"] = strategy_wallet
|
|
208
208
|
|
|
209
209
|
self.config = merged_config
|
|
210
210
|
self.simulation = simulation
|
|
211
211
|
self.balance_adapter = None
|
|
212
212
|
self.tx_adapter = None
|
|
213
213
|
self.token_adapter = None
|
|
214
|
-
self.ledger_adapter = None
|
|
215
214
|
self.evm_transaction_adapter = None
|
|
216
215
|
self.web3_service = web3_service
|
|
217
216
|
self.pool_adapter = None
|
|
@@ -220,17 +219,17 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
220
219
|
|
|
221
220
|
try:
|
|
222
221
|
main_wallet_cfg = self.config.get("main_wallet")
|
|
223
|
-
|
|
222
|
+
strategy_wallet_cfg = self.config.get("strategy_wallet")
|
|
224
223
|
|
|
225
224
|
# Validate wallets are configured
|
|
226
|
-
if not
|
|
225
|
+
if not strategy_wallet_cfg or not strategy_wallet_cfg.get("address"):
|
|
227
226
|
raise ValueError(
|
|
228
|
-
"
|
|
227
|
+
"strategy_wallet not configured. Provide strategy_wallet address in config or ensure wallet is properly configured for your wallet provider"
|
|
229
228
|
)
|
|
230
229
|
|
|
231
230
|
adapter_config = {
|
|
232
231
|
"main_wallet": main_wallet_cfg or None,
|
|
233
|
-
"
|
|
232
|
+
"strategy_wallet": strategy_wallet_cfg or None,
|
|
234
233
|
"strategy": self.config,
|
|
235
234
|
}
|
|
236
235
|
|
|
@@ -250,7 +249,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
250
249
|
token_transaction_service = web3_service.token_transactions
|
|
251
250
|
balance = BalanceAdapter(adapter_config, web3_service=web3_service)
|
|
252
251
|
token_adapter = TokenAdapter()
|
|
253
|
-
ledger_adapter = LedgerAdapter()
|
|
252
|
+
ledger_adapter = LedgerAdapter() # here
|
|
254
253
|
brap_adapter = BRAPAdapter(
|
|
255
254
|
web3_service=web3_service, simulation=self.simulation
|
|
256
255
|
)
|
|
@@ -287,26 +286,6 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
287
286
|
logger.error(f"Failed to initialize strategy adapters: {e}")
|
|
288
287
|
raise
|
|
289
288
|
|
|
290
|
-
def _get_vault_wallet_address(self) -> str:
|
|
291
|
-
"""Get vault wallet address with validation."""
|
|
292
|
-
vault_wallet = self.config.get("vault_wallet")
|
|
293
|
-
if not vault_wallet or not isinstance(vault_wallet, dict):
|
|
294
|
-
raise ValueError("vault_wallet not configured in strategy config")
|
|
295
|
-
address = vault_wallet.get("address")
|
|
296
|
-
if not address:
|
|
297
|
-
raise ValueError("vault_wallet address not found in config")
|
|
298
|
-
return str(address)
|
|
299
|
-
|
|
300
|
-
def _get_main_wallet_address(self) -> str:
|
|
301
|
-
"""Get main wallet address with validation."""
|
|
302
|
-
main_wallet = self.config.get("main_wallet")
|
|
303
|
-
if not main_wallet or not isinstance(main_wallet, dict):
|
|
304
|
-
raise ValueError("main_wallet not configured in strategy config")
|
|
305
|
-
address = main_wallet.get("address")
|
|
306
|
-
if not address:
|
|
307
|
-
raise ValueError("main_wallet address not found in config")
|
|
308
|
-
return str(address)
|
|
309
|
-
|
|
310
289
|
async def setup(self):
|
|
311
290
|
if self.token_adapter is None:
|
|
312
291
|
raise RuntimeError(
|
|
@@ -344,110 +323,11 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
344
323
|
self.hys_z: float = self.HYSTERESIS_Z
|
|
345
324
|
self.rotation_tx_cost: float = self.ROTATION_TX_COST
|
|
346
325
|
|
|
347
|
-
async def deposit(
|
|
348
|
-
self, main_token_amount: float = 0.0, gas_token_amount: float = 0.0
|
|
349
|
-
) -> StatusTuple:
|
|
350
|
-
if main_token_amount == 0.0 and gas_token_amount == 0.0:
|
|
351
|
-
return (
|
|
352
|
-
False,
|
|
353
|
-
"Either main_token_amount or gas_token_amount must be provided",
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
# Validate minimum main_token_amount
|
|
357
|
-
if main_token_amount > 0:
|
|
358
|
-
if main_token_amount < self.MIN_USDT0_DEPOSIT_AMOUNT:
|
|
359
|
-
return (
|
|
360
|
-
False,
|
|
361
|
-
f"Main token amount {main_token_amount} is below minimum {self.MIN_USDT0_DEPOSIT_AMOUNT}",
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
if gas_token_amount and gas_token_amount > self.GAS_MAXIMUM:
|
|
365
|
-
return (
|
|
366
|
-
False,
|
|
367
|
-
f"Gas token amount exceeds maximum configured gas buffer: {self.GAS_MAXIMUM}",
|
|
368
|
-
)
|
|
369
|
-
|
|
370
|
-
if self.balance_adapter is None:
|
|
371
|
-
return (
|
|
372
|
-
False,
|
|
373
|
-
"Balance adapter not initialized. Strategy initialization may have failed.",
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
(
|
|
377
|
-
success,
|
|
378
|
-
main_usdt0_balance,
|
|
379
|
-
) = await self.balance_adapter.get_balance(
|
|
380
|
-
token_id=self.usdt_token_info.get("token_id"),
|
|
381
|
-
wallet_address=self._get_main_wallet_address(),
|
|
382
|
-
)
|
|
383
|
-
if not success:
|
|
384
|
-
return (
|
|
385
|
-
False,
|
|
386
|
-
f"Failed to get main wallet USDT0 balance: {main_usdt0_balance}",
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
(
|
|
390
|
-
success,
|
|
391
|
-
main_hype_balance,
|
|
392
|
-
) = await self.balance_adapter.get_balance(
|
|
393
|
-
token_id=self.hype_token_info.get("token_id"),
|
|
394
|
-
wallet_address=self._get_main_wallet_address(),
|
|
395
|
-
)
|
|
396
|
-
if not success:
|
|
397
|
-
return (
|
|
398
|
-
False,
|
|
399
|
-
f"Failed to get main wallet HYPE balance: {main_hype_balance}",
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
main_usdt0_native = main_usdt0_balance / (
|
|
403
|
-
10 ** self.usdt_token_info.get("decimals")
|
|
404
|
-
)
|
|
405
|
-
main_hype_native = main_hype_balance / (
|
|
406
|
-
10 ** self.hype_token_info.get("decimals")
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
if main_token_amount > 0:
|
|
410
|
-
if main_usdt0_native < main_token_amount:
|
|
411
|
-
return (
|
|
412
|
-
False,
|
|
413
|
-
f"Main wallet USDT0 balance is less than the deposit amount: {main_usdt0_native} < {main_token_amount}",
|
|
414
|
-
)
|
|
415
|
-
|
|
416
|
-
if gas_token_amount > 0:
|
|
417
|
-
if main_hype_native < gas_token_amount:
|
|
418
|
-
return (
|
|
419
|
-
False,
|
|
420
|
-
f"Main wallet HYPE balance is less than the deposit amount: {main_hype_native} < {gas_token_amount}",
|
|
421
|
-
)
|
|
422
|
-
|
|
423
|
-
if gas_token_amount > 0:
|
|
424
|
-
(
|
|
425
|
-
success,
|
|
426
|
-
msg,
|
|
427
|
-
) = await self.balance_adapter.move_from_main_wallet_to_vault_wallet(
|
|
428
|
-
self.hype_token_info.get("token_id"),
|
|
429
|
-
gas_token_amount,
|
|
430
|
-
strategy_name=self.name,
|
|
431
|
-
)
|
|
432
|
-
if not success:
|
|
433
|
-
return (False, f"HYPE transfer to vault failed: {msg}")
|
|
434
|
-
|
|
435
|
-
if main_token_amount > 0:
|
|
436
|
-
(
|
|
437
|
-
success,
|
|
438
|
-
msg,
|
|
439
|
-
) = await self.balance_adapter.move_from_main_wallet_to_vault_wallet(
|
|
440
|
-
self.usdt_token_info.get("token_id"),
|
|
441
|
-
main_token_amount,
|
|
442
|
-
strategy_name=self.name,
|
|
443
|
-
)
|
|
444
|
-
if not success:
|
|
445
|
-
return (False, f"USDT0 transfer to vault failed: {msg}")
|
|
446
|
-
|
|
326
|
+
async def deposit(self) -> StatusTuple:
|
|
447
327
|
self._invalidate_assets_snapshot()
|
|
448
328
|
await self._hydrate_position_from_chain()
|
|
449
329
|
|
|
450
|
-
return (
|
|
330
|
+
return (True, "hydrated positions")
|
|
451
331
|
|
|
452
332
|
async def _estimate_redeploy_tokens(self) -> float:
|
|
453
333
|
positions = await self._get_lent_positions()
|
|
@@ -558,7 +438,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
558
438
|
|
|
559
439
|
_, snapshot = await self.hyperlend_adapter.get_assets_view(
|
|
560
440
|
chain_id=self.hype_token_info.get("chain").get("id"),
|
|
561
|
-
user_address=self.
|
|
441
|
+
user_address=self._get_strategy_wallet_address(),
|
|
562
442
|
)
|
|
563
443
|
|
|
564
444
|
assets = snapshot.get("assets_view", {}).get("assets", [])
|
|
@@ -731,7 +611,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
731
611
|
from_symbol = from_token_info.get("symbol")
|
|
732
612
|
to_symbol = to_token_info.get("symbol")
|
|
733
613
|
|
|
734
|
-
|
|
614
|
+
strategy_address = self._get_strategy_wallet_address()
|
|
735
615
|
|
|
736
616
|
retries = 7
|
|
737
617
|
while retries > 0:
|
|
@@ -745,7 +625,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
745
625
|
) = await self.brap_adapter.swap_from_token_ids(
|
|
746
626
|
from_token_id=from_token_id,
|
|
747
627
|
to_token_id=to_token_id,
|
|
748
|
-
from_address=
|
|
628
|
+
from_address=strategy_address,
|
|
749
629
|
amount=amount_wei_str,
|
|
750
630
|
slippage=slippage,
|
|
751
631
|
strategy_name=self.name,
|
|
@@ -890,7 +770,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
890
770
|
try:
|
|
891
771
|
_, total_usdt_wei = await self.balance_adapter.get_balance(
|
|
892
772
|
token_id=self.usdt_token_info.get("token_id"),
|
|
893
|
-
wallet_address=self.
|
|
773
|
+
wallet_address=self._get_strategy_wallet_address(),
|
|
894
774
|
)
|
|
895
775
|
except Exception:
|
|
896
776
|
total_usdt_wei = 0
|
|
@@ -902,14 +782,14 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
902
782
|
(
|
|
903
783
|
transfer_success,
|
|
904
784
|
transfer_message,
|
|
905
|
-
) = await self.balance_adapter.
|
|
785
|
+
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
906
786
|
self.usdt_token_info.get("token_id"),
|
|
907
787
|
total_usdt,
|
|
908
788
|
strategy_name=self.name,
|
|
909
789
|
)
|
|
910
790
|
if transfer_success:
|
|
911
791
|
messages.append(
|
|
912
|
-
f"Returned {total_usdt:.2f} {self.usdt_token_info.get('symbol')} from
|
|
792
|
+
f"Returned {total_usdt:.2f} {self.usdt_token_info.get('symbol')} from strategy wallet to main wallet"
|
|
913
793
|
)
|
|
914
794
|
else:
|
|
915
795
|
messages.append(
|
|
@@ -919,7 +799,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
919
799
|
try:
|
|
920
800
|
_, total_hype_wei = await self.balance_adapter.get_balance(
|
|
921
801
|
token_id=self.hype_token_info.get("token_id"),
|
|
922
|
-
wallet_address=self.
|
|
802
|
+
wallet_address=self._get_strategy_wallet_address(),
|
|
923
803
|
)
|
|
924
804
|
except Exception:
|
|
925
805
|
total_hype_wei = 0
|
|
@@ -932,14 +812,14 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
932
812
|
(
|
|
933
813
|
transfer_success,
|
|
934
814
|
transfer_message,
|
|
935
|
-
) = await self.balance_adapter.
|
|
815
|
+
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
936
816
|
self.hype_token_info.get("token_id"),
|
|
937
817
|
total_hype,
|
|
938
818
|
strategy_name=self.name,
|
|
939
819
|
)
|
|
940
820
|
if transfer_success:
|
|
941
821
|
messages.append(
|
|
942
|
-
f"Returned {total_hype:.2f} {self.hype_token_info.get('symbol')} from
|
|
822
|
+
f"Returned {total_hype:.2f} {self.hype_token_info.get('symbol')} from strategy wallet to main wallet"
|
|
943
823
|
)
|
|
944
824
|
else:
|
|
945
825
|
messages.append(
|
|
@@ -1013,7 +893,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1013
893
|
(
|
|
1014
894
|
success,
|
|
1015
895
|
message,
|
|
1016
|
-
) = await self.balance_adapter.
|
|
896
|
+
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
1017
897
|
token_id=token_info.get("token_id"),
|
|
1018
898
|
amount=amount_tokens,
|
|
1019
899
|
strategy_name=self.name,
|
|
@@ -1213,7 +1093,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1213
1093
|
)
|
|
1214
1094
|
|
|
1215
1095
|
last_rotation = await self._get_last_rotation_time(
|
|
1216
|
-
wallet_address=self.
|
|
1096
|
+
wallet_address=self._get_strategy_wallet_address(),
|
|
1217
1097
|
)
|
|
1218
1098
|
cooldown_notice = None
|
|
1219
1099
|
if rotation_allowed and last_rotation is not None:
|
|
@@ -1471,7 +1351,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1471
1351
|
(
|
|
1472
1352
|
transfer_success,
|
|
1473
1353
|
_,
|
|
1474
|
-
) = await self.balance_adapter.
|
|
1354
|
+
) = await self.balance_adapter.move_from_strategy_wallet_to_main_wallet(
|
|
1475
1355
|
token_id=token.get("token_id"),
|
|
1476
1356
|
amount=balance_tokens,
|
|
1477
1357
|
strategy_name=self.name,
|
|
@@ -1539,8 +1419,8 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
1539
1419
|
return actions
|
|
1540
1420
|
|
|
1541
1421
|
async def _get_last_rotation_time(self, wallet_address: str) -> datetime | None:
|
|
1542
|
-
success, data = await self.ledger_adapter.
|
|
1543
|
-
wallet_address=self.
|
|
1422
|
+
success, data = await self.ledger_adapter.get_strategy_latest_transactions(
|
|
1423
|
+
wallet_address=self._get_strategy_wallet_address(),
|
|
1544
1424
|
)
|
|
1545
1425
|
if success is False:
|
|
1546
1426
|
return None
|
|
@@ -2233,8 +2113,8 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
2233
2113
|
async def _status(self) -> StatusDict:
|
|
2234
2114
|
if not self.current_token:
|
|
2235
2115
|
await self._hydrate_position_from_chain()
|
|
2236
|
-
_, net_deposit = await self.ledger_adapter.
|
|
2237
|
-
wallet_address=self.
|
|
2116
|
+
_, net_deposit = await self.ledger_adapter.get_strategy_net_deposit(
|
|
2117
|
+
wallet_address=self._get_strategy_wallet_address()
|
|
2238
2118
|
)
|
|
2239
2119
|
snapshot = await self._get_assets_snapshot()
|
|
2240
2120
|
lent_positions = await self._get_lent_positions(snapshot)
|
|
@@ -2278,16 +2158,16 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
2278
2158
|
|
|
2279
2159
|
(
|
|
2280
2160
|
success,
|
|
2281
|
-
|
|
2161
|
+
strategy_hype_balance_wei,
|
|
2282
2162
|
) = await self.balance_adapter.get_balance(
|
|
2283
2163
|
token_id=self.hype_token_info.get("token_id"),
|
|
2284
|
-
wallet_address=self.
|
|
2164
|
+
wallet_address=self._get_strategy_wallet_address(),
|
|
2285
2165
|
)
|
|
2286
2166
|
hype_price = asset_map.get(WRAPPED_HYPE_ADDRESS, {}).get("price_usd") or 0.0
|
|
2287
2167
|
hype_value = 0.0
|
|
2288
2168
|
if hype_price and success:
|
|
2289
2169
|
hype_value = (
|
|
2290
|
-
|
|
2170
|
+
strategy_hype_balance_wei
|
|
2291
2171
|
/ (10 ** self.hype_token_info.get("decimals"))
|
|
2292
2172
|
* hype_price
|
|
2293
2173
|
)
|
|
@@ -2324,7 +2204,7 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
2324
2204
|
"lent_balance": 0.0,
|
|
2325
2205
|
"current_apy": float(self.current_avg_apy or 0.0),
|
|
2326
2206
|
"positions": position_rows,
|
|
2327
|
-
"hype_buffer_tokens":
|
|
2207
|
+
"hype_buffer_tokens": strategy_hype_balance_wei
|
|
2328
2208
|
/ (10 ** self.hype_token_info.get("decimals")),
|
|
2329
2209
|
"hype_buffer_usd": hype_value,
|
|
2330
2210
|
"idle_tokens": idle_tokens,
|
|
@@ -2367,10 +2247,10 @@ class HyperlendStableYieldStrategy(Strategy):
|
|
|
2367
2247
|
"portfolio_value": total_portfolio_value,
|
|
2368
2248
|
"net_deposit": net_deposit or 0.0,
|
|
2369
2249
|
"strategy_status": status_payload,
|
|
2370
|
-
"gas_available":
|
|
2250
|
+
"gas_available": strategy_hype_balance_wei
|
|
2371
2251
|
/ (10 ** self.hype_token_info.get("decimals")),
|
|
2372
2252
|
"gassed_up": self.GAS_MAXIMUM / 3
|
|
2373
|
-
<=
|
|
2253
|
+
<= strategy_hype_balance_wei / (10 ** self.hype_token_info.get("decimals")),
|
|
2374
2254
|
}
|
|
2375
2255
|
|
|
2376
2256
|
@staticmethod
|
|
@@ -39,13 +39,13 @@ def strategy():
|
|
|
39
39
|
"""Create a strategy instance for testing with minimal config."""
|
|
40
40
|
mock_config = {
|
|
41
41
|
"main_wallet": {"address": "0x1234567890123456789012345678901234567890"},
|
|
42
|
-
"
|
|
42
|
+
"strategy_wallet": {"address": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"},
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
s = HyperlendStableYieldStrategy(
|
|
46
46
|
config=mock_config,
|
|
47
47
|
main_wallet=mock_config["main_wallet"],
|
|
48
|
-
|
|
48
|
+
strategy_wallet=mock_config["strategy_wallet"],
|
|
49
49
|
simulation=True,
|
|
50
50
|
)
|
|
51
51
|
|
|
@@ -104,10 +104,10 @@ def strategy():
|
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
if hasattr(s, "balance_adapter") and s.balance_adapter:
|
|
107
|
-
s.balance_adapter.
|
|
107
|
+
s.balance_adapter.move_from_main_wallet_to_strategy_wallet = AsyncMock(
|
|
108
108
|
return_value=(True, "Transfer successful (simulated)")
|
|
109
109
|
)
|
|
110
|
-
s.balance_adapter.
|
|
110
|
+
s.balance_adapter.move_from_strategy_wallet_to_main_wallet = AsyncMock(
|
|
111
111
|
return_value=(True, "Transfer successful (simulated)")
|
|
112
112
|
)
|
|
113
113
|
if hasattr(s.balance_adapter, "wallet_provider"):
|
|
@@ -116,10 +116,10 @@ def strategy():
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
if hasattr(s, "ledger_adapter") and s.ledger_adapter:
|
|
119
|
-
s.ledger_adapter.
|
|
119
|
+
s.ledger_adapter.get_strategy_net_deposit = AsyncMock(
|
|
120
120
|
return_value=(True, {"net_deposit": 0})
|
|
121
121
|
)
|
|
122
|
-
s.ledger_adapter.
|
|
122
|
+
s.ledger_adapter.get_strategy_transactions = AsyncMock(
|
|
123
123
|
return_value=(True, {"transactions": []})
|
|
124
124
|
)
|
|
125
125
|
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
## What it does
|
|
9
9
|
|
|
10
|
-
Actively manages Base USDC deposits. Deposits pull USDC (plus an ETH gas buffer) from the main wallet into the
|
|
10
|
+
Actively manages Base USDC deposits. Deposits pull USDC (plus an ETH gas buffer) from the main wallet into the strategy wallet, then the strategy searches Base-native pools for the best USD-denominated APY. Updates monitor DeFi Llama feeds and Wayfinder pool analytics, respecting a rotation cooldown and minimum APY improvement before rebalancing via the BRAP router. Withdrawals unwind the current position, sweep residual tokens back into USDC, and return funds to the main wallet.
|
|
11
11
|
|
|
12
12
|
## On-chain policy
|
|
13
13
|
|
|
14
|
-
Transactions are scoped to the
|
|
14
|
+
Transactions are scoped to the strategy wallet and Enso Router approval/swap calls:
|
|
15
15
|
|
|
16
16
|
```
|
|
17
17
|
(wallet.id == 'FORMAT_WALLET_ID') && ((eth.tx.data[0..10] == '0x095ea7b3' && eth.tx.data[34..74] == 'f75584ef6673ad213a685a1b58cc0330b8ea22cf') || (eth.tx.to == '0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf'))
|
|
@@ -24,11 +24,11 @@ Transactions are scoped to the vault wallet and Enso Router approval/swap calls:
|
|
|
24
24
|
- `ROTATION_MIN_INTERVAL = 14 days` → once rotated, the strategy waits ~2 weeks unless the new candidate dramatically outperforms.
|
|
25
25
|
- `DUST_APY = 0.01` (1%) → pools below this APY are treated as dust.
|
|
26
26
|
- `SEARCH_DEPTH = 10` → how many pools to examine when selecting candidates.
|
|
27
|
-
- `MIN_GAS = 0.001` and `GAS_MAXIMUM = 0.02` Base ETH → minimum buffer required in the
|
|
27
|
+
- `MIN_GAS = 0.001` and `GAS_MAXIMUM = 0.02` Base ETH → minimum buffer required in the strategy wallet plus the upper bound accepted per deposit.
|
|
28
28
|
|
|
29
29
|
## Adapters used
|
|
30
30
|
|
|
31
|
-
- `BalanceAdapter` for wallet/pool balances and orchestrating transfers between the main and
|
|
31
|
+
- `BalanceAdapter` for wallet/pool balances and orchestrating transfers between the main and strategy wallets (with ledger recording).
|
|
32
32
|
- `PoolAdapter` for pool metadata, llama reports, and yield analytics.
|
|
33
33
|
- `BRAPAdapter` to source swap quotes and execute rotations.
|
|
34
34
|
- `TokenAdapter` for metadata (gas token, USDC info).
|
|
@@ -41,15 +41,15 @@ Transactions are scoped to the vault wallet and Enso Router approval/swap calls:
|
|
|
41
41
|
|
|
42
42
|
- Validates `main_token_amount` ≥ `MIN_AMOUNT_USDC` and `gas_token_amount` ≤ `GAS_MAXIMUM`.
|
|
43
43
|
- Confirms the main wallet holds enough USDC and Base ETH.
|
|
44
|
-
- Moves Base ETH into the
|
|
44
|
+
- Moves Base ETH into the strategy wallet (when requested or when the strategy needs a top-up), then transfers the requested USDC amount via `BalanceAdapter.move_from_main_wallet_to_strategy_wallet`.
|
|
45
45
|
- Hydrates the on-chain position snapshot so future updates know which pool is active.
|
|
46
46
|
|
|
47
47
|
### Update
|
|
48
48
|
|
|
49
|
-
- Fetches the latest
|
|
49
|
+
- Fetches the latest strategy balances, idle assets, and current target pool.
|
|
50
50
|
- Runs `_find_best_pool()` which uses `PoolAdapter` and DeFi Llama data to score up to `SEARCH_DEPTH` pools that satisfy the APY/TVL filters.
|
|
51
|
-
- Checks `LedgerAdapter.
|
|
52
|
-
- If rotation is approved, requests a BRAP quote, ensures the
|
|
51
|
+
- Checks `LedgerAdapter.get_strategy_latest_transactions()` to enforce the rotation cooldown, unless the new candidate clears the APY-improvement threshold.
|
|
52
|
+
- If rotation is approved, requests a BRAP quote, ensures the strategy has enough gas, executes the swap via `BRAPAdapter.swap_from_quote`, and sweeps any idle balances back into the target token.
|
|
53
53
|
- Records informative status messages when no better pool exists or when cooldown blocks a move.
|
|
54
54
|
|
|
55
55
|
### Status
|
|
@@ -57,13 +57,13 @@ Transactions are scoped to the vault wallet and Enso Router approval/swap calls:
|
|
|
57
57
|
`_status()` reports:
|
|
58
58
|
|
|
59
59
|
- `portfolio_value`: refreshed pool balance (in base units) converted to float.
|
|
60
|
-
- `net_deposit`: data pulled from `LedgerAdapter.
|
|
60
|
+
- `net_deposit`: data pulled from `LedgerAdapter.get_strategy_net_deposit`.
|
|
61
61
|
- `strategy_status`: dictionary exposing the active pool, APY estimates, and wallet balances.
|
|
62
62
|
|
|
63
63
|
### Withdraw
|
|
64
64
|
|
|
65
65
|
- Requires a prior deposit (the strategy tracks `self.DEPOSIT_USDC`).
|
|
66
|
-
- Reads the pool balance via `BalanceAdapter.get_pool_balance`, unwinds via BRAP swaps back to USDC, and moves USDC from the
|
|
66
|
+
- Reads the pool balance via `BalanceAdapter.get_pool_balance`, 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`.
|
|
67
67
|
- Updates the ledger and clears cached pool state.
|
|
68
68
|
|
|
69
69
|
## Running locally
|
|
@@ -90,7 +90,7 @@ poetry run python wayfinder_paths/run_strategy.py stablecoin_yield_strategy --ac
|
|
|
90
90
|
You can also load the manifest explicitly:
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
|
-
poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/
|
|
93
|
+
poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml --action status --config $(pwd)/config.json
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
Wallet addresses are auto-populated from `wallets.json` when you run `wayfinder_paths/scripts/make_wallets.py`. Set `NETWORK=testnet` in `config.json` to dry-run operations against mocked services.
|