wayfinder-paths 0.1.21__py3-none-any.whl → 0.1.23__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/__init__.py +0 -4
- wayfinder_paths/adapters/balance_adapter/README.md +0 -1
- wayfinder_paths/adapters/balance_adapter/adapter.py +65 -169
- wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -113
- wayfinder_paths/adapters/brap_adapter/README.md +22 -75
- wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
- wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
- wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +39 -86
- wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +5 -1
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +6 -5
- wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
- wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +108 -198
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +37 -23
- wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
- wayfinder_paths/core/__init__.py +0 -3
- wayfinder_paths/core/clients/BRAPClient.py +3 -0
- wayfinder_paths/core/clients/ClientManager.py +0 -7
- wayfinder_paths/core/clients/LedgerClient.py +196 -172
- wayfinder_paths/core/clients/WayfinderClient.py +0 -1
- wayfinder_paths/core/clients/__init__.py +0 -5
- wayfinder_paths/core/clients/protocols.py +0 -13
- wayfinder_paths/core/config.py +0 -164
- wayfinder_paths/core/constants/__init__.py +58 -2
- wayfinder_paths/core/constants/base.py +8 -22
- wayfinder_paths/core/constants/chains.py +36 -0
- wayfinder_paths/core/constants/contracts.py +39 -0
- wayfinder_paths/core/constants/tokens.py +9 -0
- wayfinder_paths/core/strategies/Strategy.py +0 -10
- wayfinder_paths/core/utils/evm_helpers.py +5 -15
- wayfinder_paths/core/utils/tokens.py +28 -0
- wayfinder_paths/core/utils/transaction.py +13 -7
- wayfinder_paths/core/utils/web3.py +5 -3
- wayfinder_paths/policies/enso.py +1 -2
- wayfinder_paths/policies/hyper_evm.py +6 -3
- wayfinder_paths/policies/hyperlend.py +1 -2
- wayfinder_paths/policies/moonwell.py +12 -7
- wayfinder_paths/policies/prjx.py +1 -3
- wayfinder_paths/run_strategy.py +97 -300
- wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +19 -14
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +12 -11
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +20 -33
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +21 -18
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -130
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +32 -42
- {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/METADATA +3 -4
- {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/RECORD +51 -60
- {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/WHEEL +1 -1
- wayfinder_paths/core/clients/WalletClient.py +0 -41
- wayfinder_paths/core/engine/StrategyJob.py +0 -110
- wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
- wayfinder_paths/templates/adapter/README.md +0 -150
- wayfinder_paths/templates/adapter/adapter.py +0 -16
- wayfinder_paths/templates/adapter/examples.json +0 -8
- wayfinder_paths/templates/adapter/test_adapter.py +0 -30
- wayfinder_paths/templates/strategy/README.md +0 -186
- wayfinder_paths/templates/strategy/examples.json +0 -11
- wayfinder_paths/templates/strategy/strategy.py +0 -35
- wayfinder_paths/templates/strategy/test_strategy.py +0 -166
- wayfinder_paths/tests/test_smoke_manifest.py +0 -63
- {wayfinder_paths-0.1.21.dist-info → wayfinder_paths-0.1.23.dist-info}/LICENSE +0 -0
wayfinder_paths/core/config.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
from typing import Any
|
|
5
4
|
|
|
@@ -18,157 +17,6 @@ def _load_config_file() -> dict[str, Any]:
|
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
CONFIG = _load_config_file()
|
|
21
|
-
SUPPORTED_CHAINS = [
|
|
22
|
-
1,
|
|
23
|
-
8453,
|
|
24
|
-
56,
|
|
25
|
-
42161,
|
|
26
|
-
137,
|
|
27
|
-
999,
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@dataclass
|
|
32
|
-
class SystemConfig:
|
|
33
|
-
api_base_url: str = field(default="https://api.wayfinder.ai")
|
|
34
|
-
job_id: str | None = None
|
|
35
|
-
job_type: str = "strategy"
|
|
36
|
-
update_interval: int = 60
|
|
37
|
-
max_retries: int = 3
|
|
38
|
-
retry_delay: int = 5
|
|
39
|
-
log_path: str | None = None
|
|
40
|
-
data_path: str | None = None
|
|
41
|
-
wallet_id: str | None = None
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass
|
|
45
|
-
class StrategyJobConfig:
|
|
46
|
-
system: SystemConfig
|
|
47
|
-
strategy_config: dict[str, Any] = field(default_factory=dict)
|
|
48
|
-
|
|
49
|
-
def __post_init__(self) -> None:
|
|
50
|
-
try:
|
|
51
|
-
if not isinstance(self.strategy_config, dict):
|
|
52
|
-
self.strategy_config = {}
|
|
53
|
-
|
|
54
|
-
wallet_type = self._get_wallet_type()
|
|
55
|
-
if wallet_type and wallet_type != "local":
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
by_label, by_addr = self._load_wallets_from_file()
|
|
59
|
-
|
|
60
|
-
self._enrich_wallet_addresses(by_label)
|
|
61
|
-
if wallet_type in (None, "local"):
|
|
62
|
-
self._enrich_wallet_private_keys(by_addr)
|
|
63
|
-
except Exception as e:
|
|
64
|
-
logger.warning(
|
|
65
|
-
f"Failed to enrich strategy config with wallet information: {e}"
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
def _get_wallet_type(self) -> str | None:
|
|
69
|
-
wallet_type = self.strategy_config.get("wallet_type")
|
|
70
|
-
if wallet_type:
|
|
71
|
-
return wallet_type
|
|
72
|
-
|
|
73
|
-
main_wallet = self.strategy_config.get("main_wallet")
|
|
74
|
-
if isinstance(main_wallet, dict):
|
|
75
|
-
wallet_type = main_wallet.get("wallet_type")
|
|
76
|
-
if wallet_type:
|
|
77
|
-
return wallet_type
|
|
78
|
-
|
|
79
|
-
strategy_wallet = self.strategy_config.get("strategy_wallet")
|
|
80
|
-
if isinstance(strategy_wallet, dict):
|
|
81
|
-
wallet_type = strategy_wallet.get("wallet_type")
|
|
82
|
-
if wallet_type:
|
|
83
|
-
return wallet_type
|
|
84
|
-
|
|
85
|
-
return None
|
|
86
|
-
|
|
87
|
-
def _load_wallets_from_file(
|
|
88
|
-
self,
|
|
89
|
-
) -> tuple[dict[str, dict[str, Any]], dict[str, dict[str, Any]]]:
|
|
90
|
-
entries = _read_wallets_from_config()
|
|
91
|
-
by_label: dict[str, dict[str, Any]] = {}
|
|
92
|
-
by_addr: dict[str, dict[str, Any]] = {}
|
|
93
|
-
|
|
94
|
-
if entries and isinstance(entries, list):
|
|
95
|
-
for e in entries:
|
|
96
|
-
if isinstance(e, dict):
|
|
97
|
-
label = e.get("label")
|
|
98
|
-
if isinstance(label, str):
|
|
99
|
-
by_label[label] = e
|
|
100
|
-
addr = e.get("address")
|
|
101
|
-
if isinstance(addr, str):
|
|
102
|
-
by_addr[addr.lower()] = e
|
|
103
|
-
|
|
104
|
-
return by_label, by_addr
|
|
105
|
-
|
|
106
|
-
def _enrich_wallet_addresses(self, by_label: dict[str, dict[str, Any]]) -> None:
|
|
107
|
-
if "main_wallet" not in self.strategy_config:
|
|
108
|
-
main_wallet = by_label.get("main")
|
|
109
|
-
if main_wallet:
|
|
110
|
-
self.strategy_config["main_wallet"] = {
|
|
111
|
-
"address": main_wallet["address"]
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
strategy_name = self.strategy_config.get("_strategy_name")
|
|
115
|
-
if strategy_name and isinstance(strategy_name, str):
|
|
116
|
-
strategy_wallet = by_label.get(strategy_name)
|
|
117
|
-
if strategy_wallet:
|
|
118
|
-
if "strategy_wallet" not in self.strategy_config:
|
|
119
|
-
self.strategy_config["strategy_wallet"] = {
|
|
120
|
-
"address": strategy_wallet["address"]
|
|
121
|
-
}
|
|
122
|
-
elif isinstance(self.strategy_config.get("strategy_wallet"), dict):
|
|
123
|
-
if not self.strategy_config["strategy_wallet"].get("address"):
|
|
124
|
-
self.strategy_config["strategy_wallet"]["address"] = (
|
|
125
|
-
strategy_wallet["address"]
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
def _enrich_wallet_private_keys(self, by_addr: dict[str, dict[str, Any]]) -> None:
|
|
129
|
-
try:
|
|
130
|
-
for key in ("main_wallet", "strategy_wallet"):
|
|
131
|
-
wallet_obj = self.strategy_config.get(key)
|
|
132
|
-
if isinstance(wallet_obj, dict):
|
|
133
|
-
addr = (wallet_obj.get("address") or "").lower()
|
|
134
|
-
entry = by_addr.get(addr)
|
|
135
|
-
if entry:
|
|
136
|
-
pk = entry.get("private_key") or entry.get("private_key_hex")
|
|
137
|
-
if (
|
|
138
|
-
pk
|
|
139
|
-
and not wallet_obj.get("private_key")
|
|
140
|
-
and not wallet_obj.get("private_key_hex")
|
|
141
|
-
):
|
|
142
|
-
wallet_obj["private_key_hex"] = pk
|
|
143
|
-
except Exception as e:
|
|
144
|
-
logger.warning(
|
|
145
|
-
f"Failed to enrich wallet private keys from config.json: {e}"
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
@classmethod
|
|
149
|
-
def from_dict(
|
|
150
|
-
cls, data: dict[str, Any], strategy_name: str | None = None
|
|
151
|
-
) -> "StrategyJobConfig":
|
|
152
|
-
system_data = data.get("system", {})
|
|
153
|
-
sys_cfg = SystemConfig(
|
|
154
|
-
api_base_url=system_data.get("api_base_url", "https://api.wayfinder.ai"),
|
|
155
|
-
job_id=system_data.get("job_id"),
|
|
156
|
-
job_type=system_data.get("job_type", "strategy"),
|
|
157
|
-
update_interval=system_data.get("update_interval", 60),
|
|
158
|
-
max_retries=system_data.get("max_retries", 3),
|
|
159
|
-
retry_delay=system_data.get("retry_delay", 5),
|
|
160
|
-
log_path=system_data.get("log_path"),
|
|
161
|
-
data_path=system_data.get("data_path"),
|
|
162
|
-
wallet_id=system_data.get("wallet_id"),
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
strategy_config = data.get("strategy", {})
|
|
166
|
-
if strategy_name:
|
|
167
|
-
strategy_config["_strategy_name"] = strategy_name
|
|
168
|
-
return cls(
|
|
169
|
-
system=sys_cfg,
|
|
170
|
-
strategy_config=strategy_config,
|
|
171
|
-
)
|
|
172
20
|
|
|
173
21
|
|
|
174
22
|
def set_rpc_urls(rpc_urls):
|
|
@@ -189,15 +37,3 @@ def get_api_base_url() -> str:
|
|
|
189
37
|
if api_url and isinstance(api_url, str):
|
|
190
38
|
return api_url.strip()
|
|
191
39
|
return "https://wayfinder.ai/api/v1"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def _read_wallets_from_config() -> list[dict[str, Any]]:
|
|
195
|
-
try:
|
|
196
|
-
wallets = CONFIG.get("wallets", [])
|
|
197
|
-
if isinstance(wallets, list):
|
|
198
|
-
return wallets
|
|
199
|
-
logger.warning("Wallets section in config.json is not a list")
|
|
200
|
-
return []
|
|
201
|
-
except Exception as e:
|
|
202
|
-
logger.warning(f"Failed to read wallets from config.json: {e}")
|
|
203
|
-
return []
|
|
@@ -1,17 +1,73 @@
|
|
|
1
|
-
from .base import (
|
|
2
|
-
|
|
1
|
+
from wayfinder_paths.core.constants.base import (
|
|
2
|
+
ADAPTER_BALANCE,
|
|
3
|
+
ADAPTER_BRAP,
|
|
4
|
+
ADAPTER_HYPERLEND,
|
|
5
|
+
ADAPTER_HYPERLIQUID,
|
|
6
|
+
ADAPTER_LEDGER,
|
|
7
|
+
ADAPTER_MOONWELL,
|
|
8
|
+
ADAPTER_POOL,
|
|
9
|
+
ADAPTER_TOKEN,
|
|
10
|
+
DEFAULT_HTTP_TIMEOUT,
|
|
3
11
|
DEFAULT_NATIVE_GAS_UNITS,
|
|
12
|
+
DEFAULT_PAGINATION_LIMIT,
|
|
4
13
|
DEFAULT_SLIPPAGE,
|
|
5
14
|
GAS_BUFFER_MULTIPLIER,
|
|
15
|
+
MANTISSA,
|
|
16
|
+
MAX_UINT256,
|
|
6
17
|
ONE_GWEI,
|
|
18
|
+
SECONDS_PER_YEAR,
|
|
19
|
+
SUGGESTED_GAS_PRICE_MULTIPLIER,
|
|
20
|
+
SUGGESTED_PRIORITY_FEE_MULTIPLIER,
|
|
21
|
+
)
|
|
22
|
+
from wayfinder_paths.core.constants.chains import (
|
|
23
|
+
CHAIN_CODE_TO_ID,
|
|
24
|
+
CHAIN_ID_ARBITRUM,
|
|
25
|
+
CHAIN_ID_AVALANCHE,
|
|
26
|
+
CHAIN_ID_BASE,
|
|
27
|
+
CHAIN_ID_BSC,
|
|
28
|
+
CHAIN_ID_ETHEREUM,
|
|
29
|
+
CHAIN_ID_HYPEREVM,
|
|
30
|
+
CHAIN_ID_POLYGON,
|
|
31
|
+
POA_MIDDLEWARE_CHAIN_IDS,
|
|
32
|
+
PRE_EIP_1559_CHAIN_IDS,
|
|
33
|
+
SUPPORTED_CHAINS,
|
|
34
|
+
)
|
|
35
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
36
|
+
NATIVE_TOKEN_SENTINEL,
|
|
7
37
|
ZERO_ADDRESS,
|
|
8
38
|
)
|
|
9
39
|
|
|
10
40
|
__all__ = [
|
|
41
|
+
"NATIVE_TOKEN_SENTINEL",
|
|
11
42
|
"ZERO_ADDRESS",
|
|
12
43
|
"CHAIN_CODE_TO_ID",
|
|
13
44
|
"DEFAULT_NATIVE_GAS_UNITS",
|
|
14
45
|
"GAS_BUFFER_MULTIPLIER",
|
|
15
46
|
"ONE_GWEI",
|
|
47
|
+
"SUGGESTED_PRIORITY_FEE_MULTIPLIER",
|
|
48
|
+
"SUGGESTED_GAS_PRICE_MULTIPLIER",
|
|
16
49
|
"DEFAULT_SLIPPAGE",
|
|
50
|
+
"DEFAULT_HTTP_TIMEOUT",
|
|
51
|
+
"DEFAULT_PAGINATION_LIMIT",
|
|
52
|
+
"MANTISSA",
|
|
53
|
+
"SECONDS_PER_YEAR",
|
|
54
|
+
"MAX_UINT256",
|
|
55
|
+
"ADAPTER_BALANCE",
|
|
56
|
+
"ADAPTER_BRAP",
|
|
57
|
+
"ADAPTER_MOONWELL",
|
|
58
|
+
"ADAPTER_HYPERLIQUID",
|
|
59
|
+
"ADAPTER_POOL",
|
|
60
|
+
"ADAPTER_TOKEN",
|
|
61
|
+
"ADAPTER_LEDGER",
|
|
62
|
+
"ADAPTER_HYPERLEND",
|
|
63
|
+
"CHAIN_ID_ETHEREUM",
|
|
64
|
+
"CHAIN_ID_BASE",
|
|
65
|
+
"CHAIN_ID_ARBITRUM",
|
|
66
|
+
"CHAIN_ID_BSC",
|
|
67
|
+
"CHAIN_ID_POLYGON",
|
|
68
|
+
"CHAIN_ID_AVALANCHE",
|
|
69
|
+
"CHAIN_ID_HYPEREVM",
|
|
70
|
+
"SUPPORTED_CHAINS",
|
|
71
|
+
"POA_MIDDLEWARE_CHAIN_IDS",
|
|
72
|
+
"PRE_EIP_1559_CHAIN_IDS",
|
|
17
73
|
]
|
|
@@ -1,30 +1,13 @@
|
|
|
1
|
-
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
2
|
-
|
|
3
|
-
# Chain code to EVM chain id mapping
|
|
4
|
-
CHAIN_CODE_TO_ID = {
|
|
5
|
-
"base": 8453,
|
|
6
|
-
"arbitrum": 42161,
|
|
7
|
-
"arbitrum-one": 42161,
|
|
8
|
-
"ethereum": 1,
|
|
9
|
-
"mainnet": 1,
|
|
10
|
-
"hyperevm": 999,
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
# Gas/defaults
|
|
14
1
|
DEFAULT_NATIVE_GAS_UNITS = 21000
|
|
15
|
-
# Fallback gas limit used only when RPC gas estimation fails for non-revert reasons.
|
|
16
|
-
# Must be high enough for typical DeFi interactions (lending, swaps, etc.).
|
|
17
2
|
GAS_BUFFER_MULTIPLIER = 1.1
|
|
18
3
|
ONE_GWEI = 1_000_000_000
|
|
4
|
+
SUGGESTED_PRIORITY_FEE_MULTIPLIER = 1.5
|
|
5
|
+
SUGGESTED_GAS_PRICE_MULTIPLIER = 1.5
|
|
6
|
+
|
|
19
7
|
DEFAULT_SLIPPAGE = 0.005
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
# Base L2 (and some RPC providers) can occasionally take >2 minutes to index/return receipts,
|
|
23
|
-
# even if the transaction is eventually mined. A longer timeout reduces false negatives that
|
|
24
|
-
# can lead to unsafe retry behavior (nonce gaps, duplicate swaps, etc.).
|
|
25
|
-
DEFAULT_HTTP_TIMEOUT = 30.0 # HTTP client timeout
|
|
9
|
+
DEFAULT_HTTP_TIMEOUT = 30.0
|
|
26
10
|
|
|
27
|
-
# Adapter type identifiers
|
|
28
11
|
ADAPTER_BALANCE = "BALANCE"
|
|
29
12
|
ADAPTER_BRAP = "BRAP"
|
|
30
13
|
ADAPTER_MOONWELL = "MOONWELL"
|
|
@@ -34,5 +17,8 @@ ADAPTER_TOKEN = "TOKEN"
|
|
|
34
17
|
ADAPTER_LEDGER = "LEDGER"
|
|
35
18
|
ADAPTER_HYPERLEND = "HYPERLEND"
|
|
36
19
|
|
|
37
|
-
# Pagination defaults
|
|
38
20
|
DEFAULT_PAGINATION_LIMIT = 50
|
|
21
|
+
|
|
22
|
+
MANTISSA = 10**18
|
|
23
|
+
SECONDS_PER_YEAR = 365 * 24 * 60 * 60
|
|
24
|
+
MAX_UINT256 = 2**256 - 1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
CHAIN_ID_ETHEREUM = 1
|
|
2
|
+
CHAIN_ID_BASE = 8453
|
|
3
|
+
CHAIN_ID_ARBITRUM = 42161
|
|
4
|
+
CHAIN_ID_BSC = 56
|
|
5
|
+
CHAIN_ID_POLYGON = 137
|
|
6
|
+
CHAIN_ID_AVALANCHE = 43114
|
|
7
|
+
CHAIN_ID_HYPEREVM = 999
|
|
8
|
+
|
|
9
|
+
CHAIN_CODE_TO_ID = {
|
|
10
|
+
"base": CHAIN_ID_BASE,
|
|
11
|
+
"arbitrum": CHAIN_ID_ARBITRUM,
|
|
12
|
+
"arbitrum-one": CHAIN_ID_ARBITRUM,
|
|
13
|
+
"ethereum": CHAIN_ID_ETHEREUM,
|
|
14
|
+
"mainnet": CHAIN_ID_ETHEREUM,
|
|
15
|
+
"hyperevm": CHAIN_ID_HYPEREVM,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
SUPPORTED_CHAINS = [
|
|
19
|
+
CHAIN_ID_ETHEREUM,
|
|
20
|
+
CHAIN_ID_BASE,
|
|
21
|
+
CHAIN_ID_BSC,
|
|
22
|
+
CHAIN_ID_ARBITRUM,
|
|
23
|
+
CHAIN_ID_POLYGON,
|
|
24
|
+
CHAIN_ID_HYPEREVM,
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
POA_MIDDLEWARE_CHAIN_IDS: set[int] = {
|
|
28
|
+
CHAIN_ID_BSC,
|
|
29
|
+
CHAIN_ID_POLYGON,
|
|
30
|
+
CHAIN_ID_AVALANCHE,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
PRE_EIP_1559_CHAIN_IDS: set[int] = {
|
|
34
|
+
CHAIN_ID_BSC,
|
|
35
|
+
CHAIN_ID_ARBITRUM,
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
2
|
+
NATIVE_TOKEN_SENTINEL = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
|
3
|
+
|
|
4
|
+
BASE_WETH = "0x4200000000000000000000000000000000000006"
|
|
5
|
+
BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
6
|
+
BASE_WSTETH = "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452"
|
|
7
|
+
|
|
8
|
+
MOONWELL_M_USDC = "0xEdc817A28E8B93B03976FBd4a3dDBc9f7D176c22"
|
|
9
|
+
MOONWELL_M_WETH = "0x628ff693426583D9a7FB391E54366292F509D457"
|
|
10
|
+
MOONWELL_M_WSTETH = "0x627Fe393Bc6EdDA28e99AE648fD6fF362514304b"
|
|
11
|
+
MOONWELL_COMPTROLLER = "0xfbb21d0380bee3312b33c4353c8936a0f13ef26c"
|
|
12
|
+
MOONWELL_REWARD_DISTRIBUTOR = "0xe9005b078701e2a0948d2eac43010d35870ad9d2"
|
|
13
|
+
MOONWELL_WELL_TOKEN = "0xA88594D404727625A9437C3f886C7643872296AE"
|
|
14
|
+
|
|
15
|
+
ENSO_ROUTER = "0xf75584ef6673ad213a685a1b58cc0330b8ea22cf"
|
|
16
|
+
|
|
17
|
+
HYPEREVM_WHYPE = "0x5555555555555555555555555555555555555555"
|
|
18
|
+
HYPERCORE_SENTINEL_ADDRESS = "0x2222222222222222222222222222222222222222"
|
|
19
|
+
HYPERCORE_SENTINEL_VALUE = 100_000_000_000
|
|
20
|
+
|
|
21
|
+
HYPERLEND_POOL = "0x00A89d7a5A02160f20150EbEA7a2b5E4879A1A8b"
|
|
22
|
+
HYPERLEND_WRAPPED_TOKEN_GATEWAY = "0x49558c794ea2aC8974C9F27886DDfAa951E99171"
|
|
23
|
+
|
|
24
|
+
PRJX_ROUTER = "0x1ebdfc75ffe3ba3de61e7138a3e8706ac841af9b"
|
|
25
|
+
PRJX_NPM = "0xeAd19AE861c29bBb2101E834922B2FEee69B9091"
|
|
26
|
+
|
|
27
|
+
ARBITRUM_USDC = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
|
|
28
|
+
|
|
29
|
+
HYPERLIQUID_BRIDGE = "0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7"
|
|
30
|
+
|
|
31
|
+
USDT_ETHEREUM = "0xdac17f958d2ee523a2206206994597c13d831ec7"
|
|
32
|
+
USDT_POLYGON = "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"
|
|
33
|
+
USDT_BSC = "0x55d398326f99059ff775485246999027b3197955"
|
|
34
|
+
|
|
35
|
+
TOKENS_REQUIRING_APPROVAL_RESET: set[tuple[int, str]] = {
|
|
36
|
+
(1, USDT_ETHEREUM),
|
|
37
|
+
(137, USDT_POLYGON),
|
|
38
|
+
(56, USDT_BSC),
|
|
39
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
TOKEN_ID_USDC_BASE = "usd-coin-base"
|
|
2
|
+
TOKEN_ID_WETH_BASE = "l2-standard-bridged-weth-base-base"
|
|
3
|
+
TOKEN_ID_WSTETH_BASE = "superbridge-bridged-wsteth-base-base"
|
|
4
|
+
TOKEN_ID_ETH_BASE = "ethereum-base"
|
|
5
|
+
TOKEN_ID_WELL_BASE = "moonwell-artemis-base"
|
|
6
|
+
|
|
7
|
+
TOKEN_ID_STETH = "staked-ether-ethereum"
|
|
8
|
+
|
|
9
|
+
TOKEN_ID_USDC_ARBITRUM = "usd-coin-arbitrum"
|
|
@@ -95,16 +95,6 @@ class Strategy(ABC):
|
|
|
95
95
|
pass
|
|
96
96
|
|
|
97
97
|
async def withdraw(self, **kwargs) -> StatusTuple:
|
|
98
|
-
if hasattr(self, "ledger_adapter") and self.ledger_adapter:
|
|
99
|
-
while self.ledger_adapter.positions.operations:
|
|
100
|
-
node = self.ledger_adapter.positions.operations[-1]
|
|
101
|
-
adapter = self.adapters.get(node.adapter)
|
|
102
|
-
if adapter and hasattr(adapter, "unwind_op"):
|
|
103
|
-
await adapter.unwind_op(node)
|
|
104
|
-
self.ledger_adapter.positions.operations.pop()
|
|
105
|
-
|
|
106
|
-
await self.ledger_adapter.save()
|
|
107
|
-
|
|
108
98
|
return (True, "Withdrawal complete")
|
|
109
99
|
|
|
110
100
|
@abstractmethod
|
|
@@ -4,25 +4,15 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from loguru import logger
|
|
6
6
|
|
|
7
|
-
from wayfinder_paths.core.constants.
|
|
7
|
+
from wayfinder_paths.core.constants.chains import CHAIN_CODE_TO_ID
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def
|
|
11
|
-
if not chain_code:
|
|
12
|
-
return None
|
|
13
|
-
return CHAIN_CODE_TO_ID.get(chain_code.lower())
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def resolve_chain_id(token_info: dict[str, Any], logger_instance=None) -> int | None:
|
|
17
|
-
log = logger_instance or logger
|
|
10
|
+
def resolve_chain_id(token_info: dict[str, Any]) -> int | None:
|
|
18
11
|
chain_meta = token_info.get("chain") or {}
|
|
19
12
|
chain_id = chain_meta.get("id")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
except (ValueError, TypeError):
|
|
24
|
-
log.debug("Invalid chain_id in token_info.chain: %s", chain_id)
|
|
25
|
-
return chain_code_to_chain_id(chain_meta.get("code"))
|
|
13
|
+
if chain_id is not None:
|
|
14
|
+
return int(chain_id)
|
|
15
|
+
return CHAIN_CODE_TO_ID.get(chain_meta.get("code").lower())
|
|
26
16
|
|
|
27
17
|
|
|
28
18
|
def resolve_rpc_url(
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
1
4
|
from web3 import AsyncWeb3
|
|
2
5
|
|
|
3
6
|
from wayfinder_paths.core.constants.erc20_abi import ERC20_ABI
|
|
7
|
+
from wayfinder_paths.core.utils.transaction import send_transaction
|
|
4
8
|
from wayfinder_paths.core.utils.web3 import web3_from_chain_id
|
|
5
9
|
|
|
6
10
|
NATIVE_TOKEN_ADDRESSES: set = {
|
|
@@ -102,3 +106,27 @@ async def build_send_transaction(
|
|
|
102
106
|
"data": data,
|
|
103
107
|
"chainId": chain_id,
|
|
104
108
|
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
async def ensure_allowance(
|
|
112
|
+
*,
|
|
113
|
+
token_address: str,
|
|
114
|
+
owner: str,
|
|
115
|
+
spender: str,
|
|
116
|
+
amount: int,
|
|
117
|
+
chain_id: int,
|
|
118
|
+
signing_callback: Callable,
|
|
119
|
+
approval_amount: int | None = None,
|
|
120
|
+
) -> tuple[bool, Any]:
|
|
121
|
+
allowance = await get_token_allowance(token_address, chain_id, owner, spender)
|
|
122
|
+
if allowance >= amount:
|
|
123
|
+
return True, {}
|
|
124
|
+
approve_tx = await build_approve_transaction(
|
|
125
|
+
from_address=owner,
|
|
126
|
+
chain_id=chain_id,
|
|
127
|
+
token_address=token_address,
|
|
128
|
+
spender_address=spender,
|
|
129
|
+
amount=approval_amount if approval_amount is not None else amount,
|
|
130
|
+
)
|
|
131
|
+
txn_hash = await send_transaction(approve_tx, signing_callback)
|
|
132
|
+
return True, txn_hash
|
|
@@ -4,17 +4,20 @@ from collections.abc import Callable
|
|
|
4
4
|
from loguru import logger
|
|
5
5
|
from web3 import AsyncWeb3
|
|
6
6
|
|
|
7
|
+
from wayfinder_paths.core.constants.base import (
|
|
8
|
+
SUGGESTED_GAS_PRICE_MULTIPLIER,
|
|
9
|
+
SUGGESTED_PRIORITY_FEE_MULTIPLIER,
|
|
10
|
+
)
|
|
11
|
+
from wayfinder_paths.core.constants.chains import (
|
|
12
|
+
CHAIN_ID_HYPEREVM,
|
|
13
|
+
PRE_EIP_1559_CHAIN_IDS,
|
|
14
|
+
)
|
|
7
15
|
from wayfinder_paths.core.utils.web3 import (
|
|
8
16
|
get_transaction_chain_id,
|
|
9
17
|
web3_from_chain_id,
|
|
10
18
|
web3s_from_chain_id,
|
|
11
19
|
)
|
|
12
20
|
|
|
13
|
-
PRE_EIP_1559_CHAIN_IDS: set = {56, 42161}
|
|
14
|
-
|
|
15
|
-
SUGGESTED_PRIORITY_FEE_MULTIPLIER = 1.5
|
|
16
|
-
SUGGESTED_GAS_PRICE_MULTIPLIER = 1.5
|
|
17
|
-
|
|
18
21
|
|
|
19
22
|
def _get_transaction_from_address(transaction: dict) -> str:
|
|
20
23
|
if "from" not in transaction:
|
|
@@ -71,7 +74,7 @@ async def gas_price_transaction(
|
|
|
71
74
|
gas_price = max(gas_prices)
|
|
72
75
|
|
|
73
76
|
transaction["gasPrice"] = int(gas_price * gas_price_multiplier)
|
|
74
|
-
elif chain_id ==
|
|
77
|
+
elif chain_id == CHAIN_ID_HYPEREVM:
|
|
75
78
|
# HyperEVM big blocks fetch base gas price from a different RPC method. Priority fee = 0 is # grandfathered in from Django, not sure what's right here.
|
|
76
79
|
big_block_gas_prices = await asyncio.gather(
|
|
77
80
|
*[web3.hype.big_block_gas_price() for web3 in web3s]
|
|
@@ -111,7 +114,7 @@ async def gas_limit_transaction(transaction: dict):
|
|
|
111
114
|
|
|
112
115
|
async def _estimate_gas(web3: AsyncWeb3, transaction: dict) -> int:
|
|
113
116
|
try:
|
|
114
|
-
return await web3.eth.estimate_gas(transaction, block_identifier="
|
|
117
|
+
return await web3.eth.estimate_gas(transaction, block_identifier="latest")
|
|
115
118
|
except Exception as e:
|
|
116
119
|
logger.info(
|
|
117
120
|
f"Failed to estimate gas using {web3.provider.endpoint_uri}. Error: {e}"
|
|
@@ -174,6 +177,9 @@ async def wait_for_transaction_receipt(
|
|
|
174
177
|
async def send_transaction(
|
|
175
178
|
transaction: dict, sign_callback: Callable, wait_for_receipt=True
|
|
176
179
|
) -> str:
|
|
180
|
+
if sign_callback is None:
|
|
181
|
+
raise ValueError("sign_callback must be provided to send transaction")
|
|
182
|
+
|
|
177
183
|
logger.info(f"Broadcasting transaction {transaction.get('to', 'unknown')[:10]}...")
|
|
178
184
|
chain_id = get_transaction_chain_id(transaction)
|
|
179
185
|
transaction = await gas_limit_transaction(transaction)
|
|
@@ -5,8 +5,10 @@ from web3.middleware import ExtraDataToPOAMiddleware
|
|
|
5
5
|
from web3.module import Module
|
|
6
6
|
|
|
7
7
|
from wayfinder_paths.core.config import get_rpc_urls
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
from wayfinder_paths.core.constants.chains import (
|
|
9
|
+
CHAIN_ID_HYPEREVM,
|
|
10
|
+
POA_MIDDLEWARE_CHAIN_IDS,
|
|
11
|
+
)
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class HyperModule(Module):
|
|
@@ -31,7 +33,7 @@ def _get_web3(rpc: str, chain_id: int) -> AsyncWeb3:
|
|
|
31
33
|
web3 = AsyncWeb3(AsyncHTTPProvider(rpc))
|
|
32
34
|
if chain_id in POA_MIDDLEWARE_CHAIN_IDS:
|
|
33
35
|
web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
|
34
|
-
if chain_id ==
|
|
36
|
+
if chain_id == CHAIN_ID_HYPEREVM:
|
|
35
37
|
web3.attach_modules({"hype": (HyperModule)})
|
|
36
38
|
return web3
|
|
37
39
|
|
wayfinder_paths/policies/enso.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
2
|
+
HYPERCORE_SENTINEL_ADDRESS,
|
|
3
|
+
HYPERCORE_SENTINEL_VALUE,
|
|
4
|
+
HYPEREVM_WHYPE,
|
|
5
|
+
)
|
|
1
6
|
from wayfinder_paths.policies.evm import native_transfer
|
|
2
7
|
from wayfinder_paths.policies.util import allow_functions
|
|
3
8
|
|
|
4
|
-
WHYPE_TOKEN =
|
|
5
|
-
HYPERCORE_SENTINEL_ADDRESS = "0x2222222222222222222222222222222222222222"
|
|
6
|
-
HYPERCORE_SENTINEL_VALUE = 100_000_000_000
|
|
9
|
+
WHYPE_TOKEN = HYPEREVM_WHYPE
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
def hypecore_sentinel_deposit():
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
from wayfinder_paths.core.constants.contracts import HYPERLEND_POOL
|
|
1
2
|
from wayfinder_paths.policies.util import allow_functions
|
|
2
3
|
|
|
3
|
-
HYPERLEND_POOL = "0x00A89d7a5A02160f20150EbEA7a2b5E4879A1A8b"
|
|
4
|
-
|
|
5
4
|
|
|
6
5
|
async def hyperlend_supply_and_withdraw():
|
|
7
6
|
return await allow_functions(
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
from wayfinder_paths.core.constants.contracts import (
|
|
2
|
+
BASE_WETH,
|
|
3
|
+
MOONWELL_COMPTROLLER,
|
|
4
|
+
MOONWELL_M_USDC,
|
|
5
|
+
MOONWELL_M_WETH,
|
|
6
|
+
MOONWELL_M_WSTETH,
|
|
7
|
+
)
|
|
1
8
|
from wayfinder_paths.policies.util import allow_functions
|
|
2
9
|
|
|
3
|
-
WETH =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
COMPTROLLER = "0xfbb21d0380bee3312b33c4353c8936a0f13ef26c"
|
|
10
|
+
WETH = BASE_WETH
|
|
11
|
+
M_USDC = MOONWELL_M_USDC
|
|
12
|
+
M_WETH = MOONWELL_M_WETH
|
|
13
|
+
M_WSTETH = MOONWELL_M_WSTETH
|
|
14
|
+
COMPTROLLER = MOONWELL_COMPTROLLER
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
async def weth_deposit():
|
wayfinder_paths/policies/prjx.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
from wayfinder_paths.core.constants.contracts import PRJX_NPM, PRJX_ROUTER
|
|
1
2
|
from wayfinder_paths.policies.util import allow_functions
|
|
2
3
|
|
|
3
|
-
PRJX_ROUTER = "0x1ebdfc75ffe3ba3de61e7138a3e8706ac841af9b"
|
|
4
|
-
PRJX_NPM = "0xeAd19AE861c29bBb2101E834922B2FEee69B9091"
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
async def prjx_swap():
|
|
8
6
|
return await allow_functions(
|