wayfinder-paths 0.1.3__py3-none-any.whl → 0.1.5__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 +37 -32
- wayfinder_paths/__init__.py +3 -3
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/README.md +12 -12
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/adapter.py +12 -11
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/examples.json +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/balance_adapter/test_adapter.py +12 -6
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/README.md +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/adapter.py +30 -23
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/test_adapter.py +2 -2
- wayfinder_paths/adapters/hyperlend_adapter/__init__.py +7 -0
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/adapter.py +33 -26
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/hyperlend_adapter/test_adapter.py +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/README.md +27 -40
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/adapter.py +78 -75
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/examples.json +10 -4
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +11 -0
- wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/test_adapter.py +33 -28
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/README.md +2 -14
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/adapter.py +12 -19
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/test_adapter.py +2 -2
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/README.md +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/adapter.py +8 -4
- wayfinder_paths/adapters/token_adapter/examples.json +26 -0
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/adapters → adapters}/token_adapter/test_adapter.py +1 -1
- wayfinder_paths/config.example.json +3 -1
- wayfinder_paths/core/__init__.py +3 -3
- 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/engine/manifest.py +1 -1
- 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 +123 -37
- 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/{vaults/policies → policies}/enso.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/hyper_evm.py +2 -2
- wayfinder_paths/{vaults/policies → policies}/hyperlend.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/moonwell.py +1 -1
- wayfinder_paths/{vaults/policies → policies}/prjx.py +1 -1
- wayfinder_paths/run_strategy.py +29 -27
- wayfinder_paths/scripts/create_strategy.py +3 -3
- wayfinder_paths/scripts/make_wallets.py +6 -6
- wayfinder_paths/scripts/validate_manifests.py +2 -2
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/README.md +10 -9
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/manifest.yaml +1 -1
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/strategy.py +47 -167
- wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/test_strategy.py +10 -8
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/README.md +15 -14
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/manifest.yaml +2 -2
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/strategy.py +97 -97
- wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/test_strategy.py +8 -8
- wayfinder_paths/{vaults/templates → templates}/adapter/README.md +5 -5
- wayfinder_paths/{vaults/templates → templates}/adapter/manifest.yaml +1 -1
- wayfinder_paths/{vaults/templates → templates}/adapter/test_adapter.py +1 -1
- wayfinder_paths/{vaults/templates → templates}/strategy/README.md +10 -9
- wayfinder_paths/{vaults/templates → templates}/strategy/manifest.yaml +1 -1
- wayfinder_paths/{vaults/templates → templates}/strategy/test_strategy.py +8 -8
- wayfinder_paths/tests/test_test_coverage.py +5 -5
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/METADATA +146 -69
- wayfinder_paths-0.1.5.dist-info/RECORD +126 -0
- wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +0 -7
- wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +0 -11
- wayfinder_paths/vaults/adapters/token_adapter/examples.json +0 -26
- wayfinder_paths/vaults/strategies/__init__.py +0 -0
- wayfinder_paths-0.1.3.dist-info/RECORD +0 -126
- /wayfinder_paths/{vaults → adapters}/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/brap_adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/ledger_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/pool_adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/adapters → adapters}/token_adapter/__init__.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/erc20.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/evm.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/hyperliquid.py +0 -0
- /wayfinder_paths/{vaults/policies → policies}/util.py +0 -0
- /wayfinder_paths/{vaults/adapters → strategies}/__init__.py +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/config.py +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/strategies → strategies}/stablecoin_yield_strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/adapter/adapter.py +0 -0
- /wayfinder_paths/{vaults/templates → templates}/adapter/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/strategy/examples.json +0 -0
- /wayfinder_paths/{vaults/templates → templates}/strategy/strategy.py +0 -0
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.3.dist-info → wayfinder_paths-0.1.5.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Hyperlend Stable Yield Strategy
|
|
2
2
|
|
|
3
|
-
- Entrypoint: `
|
|
3
|
+
- Entrypoint: `strategies.hyperlend_stable_yield_strategy.strategy.HyperlendStableYieldStrategy`
|
|
4
4
|
- Manifest: `manifest.yaml`
|
|
5
5
|
- Examples: `examples.json`
|
|
6
6
|
- Tests: `test_strategy.py`
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
Allocates USDT0 on HyperEVM across HyperLend stablecoin markets. The strategy:
|
|
11
11
|
|
|
12
|
-
1. Pulls USDT0 (plus a configurable HYPE gas buffer) from the main wallet into the
|
|
12
|
+
1. Pulls USDT0 (plus a configurable HYPE gas buffer) from the main wallet into the strategy wallet.
|
|
13
13
|
2. Samples HyperLend hourly rate history, applies a bootstrap tournament (horizon = 6h, blocks = 6h, 4,000 trials, 7-day half-life) to estimate which stablecoin should outperform.
|
|
14
14
|
3. Tops up the small HYPE gas buffer if needed, swaps USDT0 into the target stablecoin, and supplies it to HyperLend.
|
|
15
15
|
4. Enforces a hysteresis rotation policy so minor APY noise does not churn capital.
|
|
16
16
|
|
|
17
17
|
## Policy
|
|
18
18
|
|
|
19
|
-
The manifest policy simply locks transactions to the
|
|
19
|
+
The manifest policy simply locks transactions to the strategy wallet ID:
|
|
20
20
|
|
|
21
21
|
```
|
|
22
22
|
(wallet.id == 'FORMAT_WALLET_ID')
|
|
@@ -46,14 +46,14 @@ The manifest policy simply locks transactions to the vault wallet ID:
|
|
|
46
46
|
### Deposit
|
|
47
47
|
|
|
48
48
|
- Validates USDT0 and HYPE balances in the main wallet.
|
|
49
|
-
- Transfers HYPE into the
|
|
50
|
-
- Moves USDT0 from the main wallet into the
|
|
49
|
+
- Transfers HYPE into the strategy wallet when a top-up is required, ensuring the strategy maintains the configured buffer.
|
|
50
|
+
- Moves USDT0 from the main wallet into the strategy wallet through `BalanceAdapter.move_from_main_wallet_to_strategy_wallet`.
|
|
51
51
|
- Clears cached asset snapshots so the next update starts from on-chain reality.
|
|
52
52
|
|
|
53
53
|
### Update
|
|
54
54
|
|
|
55
55
|
- Refreshes HyperLend asset snapshots, calculates tournament winners, and filters markets that respect supply caps + buffer requirements.
|
|
56
|
-
- Reads rotation history through `LedgerAdapter.
|
|
56
|
+
- Reads rotation history through `LedgerAdapter.get_strategy_latest_transactions` to enforce the cooldown (unless the short-circuit policy is triggered).
|
|
57
57
|
- If a new asset wins the tournament and passes hysteresis checks, BRAP quotes are fetched and executed to rotate into the better performer.
|
|
58
58
|
- Sweeps residual stable balances, lends via `HyperlendAdapter`, and records ledger operations.
|
|
59
59
|
|
|
@@ -75,8 +75,9 @@ The manifest policy simply locks transactions to the vault wallet ID:
|
|
|
75
75
|
# Install dependencies
|
|
76
76
|
poetry install
|
|
77
77
|
|
|
78
|
-
# Generate
|
|
79
|
-
|
|
78
|
+
# Generate main wallet (writes wallets.json)
|
|
79
|
+
# Creates a main wallet (or use 'just create-strategy' which auto-creates wallets)
|
|
80
|
+
poetry run python wayfinder_paths/scripts/make_wallets.py -n 1
|
|
80
81
|
|
|
81
82
|
# Copy config and edit credentials (or rely on env vars)
|
|
82
83
|
cp wayfinder_paths/config.example.json config.json
|
|
@@ -93,7 +94,7 @@ poetry run python wayfinder_paths/run_strategy.py hyperlend_stable_yield_strateg
|
|
|
93
94
|
Use the manifest directly if you prefer:
|
|
94
95
|
|
|
95
96
|
```bash
|
|
96
|
-
poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/
|
|
97
|
+
poetry run python wayfinder_paths/run_strategy.py --manifest wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml --action status --config $(pwd)/config.json
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
Wallet addresses/labels are auto-resolved from `wallets.json`. Set `NETWORK=testnet` in your config to run the orchestration without touching live HyperEVM endpoints.
|
wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/manifest.yaml
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
schema_version: "0.1"
|
|
2
|
-
entrypoint: "
|
|
2
|
+
entrypoint: "strategies.hyperlend_stable_yield_strategy.strategy.HyperlendStableYieldStrategy"
|
|
3
3
|
permissions:
|
|
4
4
|
policy: "(wallet.id == 'FORMAT_WALLET_ID')"
|
|
5
5
|
adapters:
|
wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/strategy.py
RENAMED
|
@@ -11,6 +11,11 @@ import pandas as pd
|
|
|
11
11
|
from loguru import logger
|
|
12
12
|
from web3 import Web3
|
|
13
13
|
|
|
14
|
+
from wayfinder_paths.adapters.balance_adapter.adapter import BalanceAdapter
|
|
15
|
+
from wayfinder_paths.adapters.brap_adapter.adapter import BRAPAdapter
|
|
16
|
+
from wayfinder_paths.adapters.hyperlend_adapter.adapter import HyperlendAdapter
|
|
17
|
+
from wayfinder_paths.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
18
|
+
from wayfinder_paths.adapters.token_adapter.adapter import TokenAdapter
|
|
14
19
|
from wayfinder_paths.core.constants.base import DEFAULT_SLIPPAGE
|
|
15
20
|
from wayfinder_paths.core.services.base import Web3Service
|
|
16
21
|
from wayfinder_paths.core.services.local_token_txn import (
|
|
@@ -27,26 +32,21 @@ from wayfinder_paths.core.strategies.descriptors import (
|
|
|
27
32
|
)
|
|
28
33
|
from wayfinder_paths.core.strategies.Strategy import StatusDict, StatusTuple, Strategy
|
|
29
34
|
from wayfinder_paths.core.wallets.WalletManager import WalletManager
|
|
30
|
-
from wayfinder_paths.
|
|
31
|
-
from wayfinder_paths.
|
|
32
|
-
from wayfinder_paths.
|
|
33
|
-
from wayfinder_paths.vaults.adapters.ledger_adapter.adapter import LedgerAdapter
|
|
34
|
-
from wayfinder_paths.vaults.adapters.token_adapter.adapter import TokenAdapter
|
|
35
|
-
from wayfinder_paths.vaults.policies.enso import ENSO_ROUTER, enso_swap
|
|
36
|
-
from wayfinder_paths.vaults.policies.erc20 import erc20_spender_for_any_token
|
|
37
|
-
from wayfinder_paths.vaults.policies.hyper_evm import (
|
|
35
|
+
from wayfinder_paths.policies.enso import ENSO_ROUTER, enso_swap
|
|
36
|
+
from wayfinder_paths.policies.erc20 import erc20_spender_for_any_token
|
|
37
|
+
from wayfinder_paths.policies.hyper_evm import (
|
|
38
38
|
hypecore_sentinel_deposit,
|
|
39
39
|
whype_deposit_and_withdraw,
|
|
40
40
|
)
|
|
41
|
-
from wayfinder_paths.
|
|
41
|
+
from wayfinder_paths.policies.hyperlend import (
|
|
42
42
|
HYPERLEND_POOL,
|
|
43
43
|
hyperlend_supply_and_withdraw,
|
|
44
44
|
)
|
|
45
|
-
from wayfinder_paths.
|
|
45
|
+
from wayfinder_paths.policies.hyperliquid import (
|
|
46
46
|
any_hyperliquid_l1_payload,
|
|
47
47
|
any_hyperliquid_user_payload,
|
|
48
48
|
)
|
|
49
|
-
from wayfinder_paths.
|
|
49
|
+
from wayfinder_paths.policies.prjx import PRJX_ROUTER, prjx_swap
|
|
50
50
|
|
|
51
51
|
SYMBOL_TRANSLATION_TABLE = str.maketrans(
|
|
52
52
|
{
|
|
@@ -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
|
wayfinder_paths/{vaults/strategies → strategies}/hyperlend_stable_yield_strategy/test_strategy.py
RENAMED
|
@@ -4,7 +4,7 @@ from unittest.mock import AsyncMock
|
|
|
4
4
|
|
|
5
5
|
# Ensure wayfinder-paths is on path for tests.test_utils import
|
|
6
6
|
# This is a workaround until conftest loading order is resolved
|
|
7
|
-
_wayfinder_path_dir = Path(__file__).parent.parent.parent.
|
|
7
|
+
_wayfinder_path_dir = Path(__file__).parent.parent.parent.resolve()
|
|
8
8
|
_wayfinder_path_str = str(_wayfinder_path_dir)
|
|
9
9
|
if _wayfinder_path_str not in sys.path:
|
|
10
10
|
sys.path.insert(0, _wayfinder_path_str)
|
|
@@ -29,7 +29,7 @@ except ImportError:
|
|
|
29
29
|
get_canonical_examples = test_utils.get_canonical_examples
|
|
30
30
|
load_strategy_examples = test_utils.load_strategy_examples
|
|
31
31
|
|
|
32
|
-
from wayfinder_paths.
|
|
32
|
+
from wayfinder_paths.strategies.hyperlend_stable_yield_strategy.strategy import ( # noqa: E402
|
|
33
33
|
HyperlendStableYieldStrategy,
|
|
34
34
|
)
|
|
35
35
|
|
|
@@ -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
|
|
|
@@ -244,6 +244,8 @@ async def test_smoke(strategy):
|
|
|
244
244
|
examples = load_strategy_examples(Path(__file__))
|
|
245
245
|
smoke_data = examples["smoke"]
|
|
246
246
|
|
|
247
|
+
await strategy.setup()
|
|
248
|
+
|
|
247
249
|
st = await strategy.status()
|
|
248
250
|
assert isinstance(st, dict)
|
|
249
251
|
assert "portfolio_value" in st or "net_deposit" in st or "strategy_status" in st
|