wayfinder-paths 0.1.8__py3-none-any.whl → 0.1.10__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 +6 -15
- wayfinder_paths/adapters/balance_adapter/README.md +1 -2
- wayfinder_paths/adapters/balance_adapter/adapter.py +4 -4
- wayfinder_paths/adapters/brap_adapter/README.md +1 -1
- wayfinder_paths/adapters/brap_adapter/adapter.py +139 -74
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +0 -7
- wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +0 -54
- wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +1 -1
- wayfinder_paths/adapters/ledger_adapter/README.md +1 -1
- wayfinder_paths/adapters/moonwell_adapter/README.md +174 -0
- wayfinder_paths/adapters/moonwell_adapter/__init__.py +7 -0
- wayfinder_paths/adapters/moonwell_adapter/adapter.py +1226 -0
- wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +635 -0
- wayfinder_paths/adapters/pool_adapter/README.md +1 -77
- wayfinder_paths/adapters/pool_adapter/adapter.py +0 -122
- wayfinder_paths/adapters/pool_adapter/examples.json +0 -57
- wayfinder_paths/adapters/pool_adapter/test_adapter.py +0 -86
- wayfinder_paths/adapters/token_adapter/README.md +1 -1
- wayfinder_paths/core/clients/ClientManager.py +1 -22
- wayfinder_paths/core/clients/WalletClient.py +0 -8
- wayfinder_paths/core/clients/WayfinderClient.py +7 -12
- wayfinder_paths/core/clients/__init__.py +0 -8
- wayfinder_paths/core/clients/protocols.py +0 -60
- wayfinder_paths/core/config.py +5 -45
- wayfinder_paths/core/constants/__init__.py +0 -2
- wayfinder_paths/core/constants/base.py +6 -2
- wayfinder_paths/core/constants/moonwell_abi.py +411 -0
- wayfinder_paths/core/services/base.py +7 -1
- wayfinder_paths/core/services/local_evm_txn.py +223 -222
- wayfinder_paths/core/services/local_token_txn.py +103 -92
- wayfinder_paths/core/services/web3_service.py +0 -2
- wayfinder_paths/core/settings.py +8 -8
- wayfinder_paths/core/strategies/Strategy.py +1 -5
- wayfinder_paths/core/strategies/descriptors.py +1 -1
- wayfinder_paths/core/utils/evm_helpers.py +7 -12
- wayfinder_paths/core/wallets/README.md +3 -6
- wayfinder_paths/run_strategy.py +62 -105
- wayfinder_paths/scripts/create_strategy.py +2 -27
- wayfinder_paths/scripts/make_wallets.py +1 -25
- wayfinder_paths/scripts/run_strategy.py +37 -9
- wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1 -3
- wayfinder_paths/strategies/basis_trading_strategy/strategy.py +87 -138
- wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +96 -58
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +2 -17
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/examples.json +4 -1
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +107 -29
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +53 -14
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +108 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +11 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2975 -0
- wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +886 -0
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +2 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +0 -4
- wayfinder_paths/templates/adapter/README.md +5 -21
- wayfinder_paths/templates/adapter/adapter.py +1 -2
- wayfinder_paths/templates/adapter/test_adapter.py +1 -1
- wayfinder_paths/templates/strategy/README.md +4 -21
- wayfinder_paths/templates/strategy/test_strategy.py +0 -4
- wayfinder_paths/tests/test_smoke_manifest.py +17 -2
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/METADATA +64 -201
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/RECORD +64 -71
- wayfinder_paths/adapters/balance_adapter/manifest.yaml +0 -8
- wayfinder_paths/adapters/brap_adapter/manifest.yaml +0 -11
- wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +0 -10
- wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +0 -8
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +0 -11
- wayfinder_paths/adapters/pool_adapter/manifest.yaml +0 -10
- wayfinder_paths/adapters/token_adapter/manifest.yaml +0 -6
- wayfinder_paths/core/clients/SimulationClient.py +0 -192
- wayfinder_paths/core/clients/TransactionClient.py +0 -63
- wayfinder_paths/core/engine/manifest.py +0 -97
- wayfinder_paths/scripts/validate_manifests.py +0 -213
- wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +0 -23
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +0 -7
- wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +0 -17
- wayfinder_paths/templates/adapter/manifest.yaml +0 -6
- wayfinder_paths/templates/strategy/manifest.yaml +0 -8
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.8.dist-info → wayfinder_paths-0.1.10.dist-info}/WHEEL +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
from decimal import ROUND_DOWN, Decimal
|
|
3
5
|
from typing import Any
|
|
4
6
|
|
|
5
7
|
from eth_utils import to_checksum_address
|
|
6
8
|
from loguru import logger
|
|
7
|
-
from web3 import AsyncWeb3
|
|
9
|
+
from web3 import AsyncWeb3, Web3
|
|
8
10
|
|
|
9
11
|
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
10
|
-
from wayfinder_paths.core.clients.TransactionClient import TransactionClient
|
|
11
12
|
from wayfinder_paths.core.constants import ZERO_ADDRESS
|
|
12
|
-
from wayfinder_paths.core.constants.erc20_abi import ERC20_APPROVAL_ABI
|
|
13
|
+
from wayfinder_paths.core.constants.erc20_abi import ERC20_ABI, ERC20_APPROVAL_ABI
|
|
13
14
|
from wayfinder_paths.core.services.base import EvmTxn, TokenTxn
|
|
14
15
|
from wayfinder_paths.core.utils.evm_helpers import resolve_chain_id
|
|
15
16
|
|
|
@@ -22,13 +23,11 @@ class LocalTokenTxnService(TokenTxn):
|
|
|
22
23
|
config: dict[str, Any] | None,
|
|
23
24
|
*,
|
|
24
25
|
wallet_provider: EvmTxn,
|
|
25
|
-
simulation: bool = False,
|
|
26
26
|
) -> None:
|
|
27
|
-
del config
|
|
27
|
+
del config
|
|
28
28
|
self.wallet_provider = wallet_provider
|
|
29
29
|
self.logger = logger.bind(service="DefaultEvmTransactionService")
|
|
30
30
|
self.token_client = TokenClient()
|
|
31
|
-
self.builder = _EvmTransactionBuilder()
|
|
32
31
|
|
|
33
32
|
async def build_send(
|
|
34
33
|
self,
|
|
@@ -51,14 +50,24 @@ class LocalTokenTxnService(TokenTxn):
|
|
|
51
50
|
return False, f"Token {token_id} is missing a chain id"
|
|
52
51
|
|
|
53
52
|
token_address = (token_meta or {}).get("address") or ZERO_ADDRESS
|
|
53
|
+
is_native = not token_address or token_address.lower() == ZERO_ADDRESS.lower()
|
|
54
|
+
|
|
55
|
+
if is_native:
|
|
56
|
+
amount_wei = self._to_base_units(
|
|
57
|
+
amount, 18
|
|
58
|
+
) # Native tokens use 18 decimals
|
|
59
|
+
else:
|
|
60
|
+
decimals = int((token_meta or {}).get("decimals") or 18)
|
|
61
|
+
amount_wei = self._to_base_units(amount, decimals)
|
|
54
62
|
|
|
55
63
|
try:
|
|
56
|
-
tx = await self.
|
|
64
|
+
tx = await self.build_send_transaction(
|
|
57
65
|
from_address=from_address,
|
|
58
66
|
to_address=to_address,
|
|
59
67
|
token_address=token_address,
|
|
60
|
-
amount=
|
|
68
|
+
amount=amount_wei,
|
|
61
69
|
chain_id=int(chain_id),
|
|
70
|
+
is_native=is_native,
|
|
62
71
|
)
|
|
63
72
|
except Exception as exc: # noqa: BLE001
|
|
64
73
|
return False, f"Failed to build send transaction: {exc}"
|
|
@@ -84,7 +93,7 @@ class LocalTokenTxnService(TokenTxn):
|
|
|
84
93
|
except (TypeError, ValueError) as exc:
|
|
85
94
|
return False, str(exc)
|
|
86
95
|
|
|
87
|
-
approve_tx = self.
|
|
96
|
+
approve_tx = self.build_erc20_approval_transaction(
|
|
88
97
|
chain_id=chain_id,
|
|
89
98
|
token_address=token_checksum,
|
|
90
99
|
from_address=from_checksum,
|
|
@@ -95,28 +104,53 @@ class LocalTokenTxnService(TokenTxn):
|
|
|
95
104
|
return True, approve_tx
|
|
96
105
|
|
|
97
106
|
async def read_erc20_allowance(
|
|
98
|
-
self,
|
|
107
|
+
self,
|
|
108
|
+
chain: Any,
|
|
109
|
+
token_address: str,
|
|
110
|
+
from_address: str,
|
|
111
|
+
spender_address: str,
|
|
112
|
+
max_retries: int = 3,
|
|
99
113
|
) -> dict[str, Any]:
|
|
100
114
|
try:
|
|
101
115
|
chain_id = self._chain_id(chain)
|
|
102
116
|
except (TypeError, ValueError) as exc:
|
|
103
117
|
return {"error": str(exc), "allowance": 0}
|
|
104
118
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
last_error = None
|
|
120
|
+
for attempt in range(max_retries):
|
|
121
|
+
w3 = self.wallet_provider.get_web3(chain_id)
|
|
122
|
+
try:
|
|
123
|
+
contract = w3.eth.contract(
|
|
124
|
+
address=to_checksum_address(token_address), abi=ERC20_APPROVAL_ABI
|
|
125
|
+
)
|
|
126
|
+
allowance = await contract.functions.allowance(
|
|
127
|
+
to_checksum_address(from_address),
|
|
128
|
+
to_checksum_address(spender_address),
|
|
129
|
+
).call()
|
|
130
|
+
return {"allowance": int(allowance)}
|
|
131
|
+
except Exception as exc: # noqa: BLE001
|
|
132
|
+
last_error = exc
|
|
133
|
+
error_str = str(exc)
|
|
134
|
+
if "429" in error_str or "Too Many Requests" in error_str:
|
|
135
|
+
if attempt < max_retries - 1:
|
|
136
|
+
wait_time = 3.0 * (2**attempt) # 3, 6, 12 seconds
|
|
137
|
+
self.logger.warning(
|
|
138
|
+
f"Rate limited reading allowance, retrying in {wait_time}s..."
|
|
139
|
+
)
|
|
140
|
+
await asyncio.sleep(wait_time)
|
|
141
|
+
continue
|
|
142
|
+
self.logger.error(f"Failed to read allowance: {exc}")
|
|
143
|
+
return {"error": f"Allowance query failed: {exc}", "allowance": 0}
|
|
144
|
+
finally:
|
|
145
|
+
await self.wallet_provider._close_web3(w3)
|
|
146
|
+
|
|
147
|
+
self.logger.error(
|
|
148
|
+
f"Failed to read allowance after {max_retries} retries: {last_error}"
|
|
149
|
+
)
|
|
150
|
+
return {
|
|
151
|
+
"error": f"Allowance query failed after retries: {last_error}",
|
|
152
|
+
"allowance": 0,
|
|
153
|
+
}
|
|
120
154
|
|
|
121
155
|
def _chain_id(self, chain: Any) -> int:
|
|
122
156
|
if isinstance(chain, dict):
|
|
@@ -127,12 +161,13 @@ class LocalTokenTxnService(TokenTxn):
|
|
|
127
161
|
raise ValueError("Chain ID is required")
|
|
128
162
|
return int(chain_id)
|
|
129
163
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
164
|
+
def _to_base_units(self, amount: float, decimals: int) -> int:
|
|
165
|
+
"""Convert human-readable amount to base units (wei for native, token units for ERC20)."""
|
|
166
|
+
scale = Decimal(10) ** int(decimals)
|
|
167
|
+
quantized = (Decimal(str(amount)) * scale).to_integral_value(
|
|
168
|
+
rounding=ROUND_DOWN
|
|
169
|
+
)
|
|
170
|
+
return int(quantized)
|
|
136
171
|
|
|
137
172
|
async def build_send_transaction(
|
|
138
173
|
self,
|
|
@@ -140,22 +175,37 @@ class _EvmTransactionBuilder:
|
|
|
140
175
|
from_address: str,
|
|
141
176
|
to_address: str,
|
|
142
177
|
token_address: str | None,
|
|
143
|
-
amount:
|
|
178
|
+
amount: int,
|
|
144
179
|
chain_id: int,
|
|
180
|
+
is_native: bool,
|
|
145
181
|
) -> dict[str, Any]:
|
|
146
182
|
"""Build the transaction dict for sending native or ERC20 tokens."""
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
183
|
+
from_checksum = to_checksum_address(from_address)
|
|
184
|
+
to_checksum = to_checksum_address(to_address)
|
|
185
|
+
chain_id_int = int(chain_id)
|
|
186
|
+
|
|
187
|
+
if is_native:
|
|
188
|
+
return {
|
|
189
|
+
"chainId": chain_id_int,
|
|
190
|
+
"from": from_checksum,
|
|
191
|
+
"to": to_checksum,
|
|
192
|
+
"value": int(amount),
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
token_checksum = to_checksum_address(token_address or ZERO_ADDRESS)
|
|
196
|
+
w3_sync = Web3()
|
|
197
|
+
contract = w3_sync.eth.contract(address=token_checksum, abi=ERC20_ABI)
|
|
198
|
+
data = contract.functions.transfer(
|
|
199
|
+
to_checksum, int(amount)
|
|
200
|
+
)._encode_transaction_data()
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
"chainId": chain_id_int,
|
|
204
|
+
"from": from_checksum,
|
|
205
|
+
"to": token_checksum,
|
|
206
|
+
"data": data,
|
|
207
|
+
"value": 0,
|
|
208
|
+
}
|
|
159
209
|
|
|
160
210
|
def build_erc20_approval_transaction(
|
|
161
211
|
self,
|
|
@@ -168,15 +218,20 @@ class _EvmTransactionBuilder:
|
|
|
168
218
|
web3: AsyncWeb3,
|
|
169
219
|
) -> dict[str, Any]:
|
|
170
220
|
"""Build an ERC20 approval transaction dict."""
|
|
221
|
+
del web3 # Use sync Web3 for encoding (AsyncContract doesn't have encodeABI)
|
|
171
222
|
token_checksum = to_checksum_address(token_address)
|
|
172
223
|
spender_checksum = to_checksum_address(spender)
|
|
173
224
|
from_checksum = to_checksum_address(from_address)
|
|
174
225
|
amount_int = int(amount)
|
|
175
226
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
227
|
+
# Use synchronous Web3 for encoding (encodeABI doesn't exist in web3.py v7)
|
|
228
|
+
w3_sync = Web3()
|
|
229
|
+
contract = w3_sync.eth.contract(address=token_checksum, abi=ERC20_APPROVAL_ABI)
|
|
230
|
+
|
|
231
|
+
# In web3.py v7, use _encode_transaction_data to encode without network calls
|
|
232
|
+
data = contract.functions.approve(
|
|
233
|
+
spender_checksum, amount_int
|
|
234
|
+
)._encode_transaction_data()
|
|
180
235
|
|
|
181
236
|
return {
|
|
182
237
|
"chainId": int(chain_id),
|
|
@@ -185,47 +240,3 @@ class _EvmTransactionBuilder:
|
|
|
185
240
|
"data": data,
|
|
186
241
|
"value": 0,
|
|
187
242
|
}
|
|
188
|
-
|
|
189
|
-
def _payload_to_tx(
|
|
190
|
-
self, payload: dict[str, Any], from_address: str, is_native: bool
|
|
191
|
-
) -> dict[str, Any]:
|
|
192
|
-
data_root = payload.get("data", payload)
|
|
193
|
-
tx_src = data_root.get("transaction") or data_root
|
|
194
|
-
|
|
195
|
-
chain_id = tx_src.get("chainId") or data_root.get("chain_id")
|
|
196
|
-
if chain_id is None:
|
|
197
|
-
raise ValueError("Transaction payload missing chainId")
|
|
198
|
-
|
|
199
|
-
tx: dict[str, Any] = {"chainId": int(chain_id)}
|
|
200
|
-
tx["from"] = to_checksum_address(from_address)
|
|
201
|
-
|
|
202
|
-
if tx_src.get("to"):
|
|
203
|
-
tx["to"] = to_checksum_address(tx_src["to"])
|
|
204
|
-
if tx_src.get("data"):
|
|
205
|
-
data = tx_src["data"]
|
|
206
|
-
tx["data"] = data if str(data).startswith("0x") else f"0x{data}"
|
|
207
|
-
|
|
208
|
-
val = tx_src.get("value", 0)
|
|
209
|
-
tx["value"] = self._normalize_value(val) if is_native else 0
|
|
210
|
-
|
|
211
|
-
if tx_src.get("gas"):
|
|
212
|
-
tx["gas"] = int(tx_src["gas"])
|
|
213
|
-
if tx_src.get("maxFeePerGas"):
|
|
214
|
-
tx["maxFeePerGas"] = int(tx_src["maxFeePerGas"])
|
|
215
|
-
if tx_src.get("maxPriorityFeePerGas"):
|
|
216
|
-
tx["maxPriorityFeePerGas"] = int(tx_src["maxPriorityFeePerGas"])
|
|
217
|
-
if tx_src.get("gasPrice"):
|
|
218
|
-
tx["gasPrice"] = int(tx_src["gasPrice"])
|
|
219
|
-
if tx_src.get("nonce") is not None:
|
|
220
|
-
tx["nonce"] = int(tx_src["nonce"])
|
|
221
|
-
|
|
222
|
-
return tx
|
|
223
|
-
|
|
224
|
-
def _normalize_value(self, value: Any) -> int:
|
|
225
|
-
if isinstance(value, str):
|
|
226
|
-
if value.startswith("0x"):
|
|
227
|
-
return int(value, 16)
|
|
228
|
-
return int(float(value))
|
|
229
|
-
if isinstance(value, (int, float)):
|
|
230
|
-
return int(value)
|
|
231
|
-
return 0
|
|
@@ -16,7 +16,6 @@ class DefaultWeb3Service(Web3Service):
|
|
|
16
16
|
*,
|
|
17
17
|
wallet_provider: EvmTxn | None = None,
|
|
18
18
|
evm_transactions: TokenTxn | None = None,
|
|
19
|
-
simulation: bool = False,
|
|
20
19
|
) -> None:
|
|
21
20
|
"""
|
|
22
21
|
Initialize the service with optional dependency injection.
|
|
@@ -33,7 +32,6 @@ class DefaultWeb3Service(Web3Service):
|
|
|
33
32
|
self._evm_transactions = LocalTokenTxnService(
|
|
34
33
|
config=cfg,
|
|
35
34
|
wallet_provider=self._wallet_provider,
|
|
36
|
-
simulation=simulation,
|
|
37
35
|
)
|
|
38
36
|
|
|
39
37
|
@property
|
wayfinder_paths/core/settings.py
CHANGED
|
@@ -12,14 +12,14 @@ class CoreSettings(BaseSettings):
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
model_config = SettingsConfigDict(
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
# Note: .env file is only used for publishing (REPOSITORY_NAME, PUBLISH_TOKEN)
|
|
16
|
+
# All other configuration should come from config.json
|
|
17
17
|
case_sensitive=False,
|
|
18
18
|
extra="ignore", # Ignore extra environment variables (e.g., from Django)
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
# Core API Configuration
|
|
22
|
-
API_ENV: str = Field("development"
|
|
22
|
+
API_ENV: str = Field(default="development")
|
|
23
23
|
|
|
24
24
|
def _compute_default_api_url() -> str:
|
|
25
25
|
"""
|
|
@@ -44,17 +44,17 @@ class CoreSettings(BaseSettings):
|
|
|
44
44
|
base = "https://wayfinder.ai/api/v1"
|
|
45
45
|
return base
|
|
46
46
|
|
|
47
|
-
WAYFINDER_API_URL: str = Field(_compute_default_api_url
|
|
47
|
+
WAYFINDER_API_URL: str = Field(default_factory=_compute_default_api_url)
|
|
48
48
|
|
|
49
49
|
# Network Configuration
|
|
50
|
-
NETWORK: str = Field("testnet"
|
|
50
|
+
NETWORK: str = Field(default="testnet") # mainnet, testnet, devnet
|
|
51
51
|
|
|
52
52
|
# Logging
|
|
53
|
-
LOG_LEVEL: str = Field("INFO"
|
|
54
|
-
LOG_FILE: str = Field("logs/strategy.log"
|
|
53
|
+
LOG_LEVEL: str = Field(default="INFO")
|
|
54
|
+
LOG_FILE: str = Field(default="logs/strategy.log")
|
|
55
55
|
|
|
56
56
|
# Safety
|
|
57
|
-
DRY_RUN: bool = Field(False
|
|
57
|
+
DRY_RUN: bool = Field(default=False)
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
# Core settings instance
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
import traceback
|
|
5
4
|
from abc import ABC, abstractmethod
|
|
6
5
|
from collections.abc import Awaitable, Callable
|
|
@@ -57,16 +56,13 @@ class Strategy(ABC):
|
|
|
57
56
|
*,
|
|
58
57
|
main_wallet: WalletConfig | dict[str, Any] | None = None,
|
|
59
58
|
strategy_wallet: WalletConfig | dict[str, Any] | None = None,
|
|
60
|
-
simulation: bool = False,
|
|
61
59
|
web3_service: Web3Service | None = None,
|
|
62
60
|
api_key: str | None = None,
|
|
63
61
|
):
|
|
64
62
|
self.adapters = {}
|
|
65
63
|
self.ledger_adapter = None
|
|
66
64
|
self.logger = logger.bind(strategy=self.__class__.__name__)
|
|
67
|
-
|
|
68
|
-
os.environ["WAYFINDER_API_KEY"] = api_key
|
|
69
|
-
|
|
65
|
+
# Note: api_key is passed to ClientManager, not set in environment
|
|
70
66
|
self.config = config
|
|
71
67
|
|
|
72
68
|
async def setup(self) -> None:
|
|
@@ -59,7 +59,7 @@ def resolve_rpc_url(
|
|
|
59
59
|
explicit_rpc_url: str | None = None,
|
|
60
60
|
) -> str:
|
|
61
61
|
"""
|
|
62
|
-
Resolve RPC URL from config
|
|
62
|
+
Resolve RPC URL from config.
|
|
63
63
|
|
|
64
64
|
Args:
|
|
65
65
|
chain_id: Chain ID to look up RPC URL for
|
|
@@ -83,10 +83,7 @@ def resolve_rpc_url(
|
|
|
83
83
|
by_str = mapping.get(str(chain_id))
|
|
84
84
|
if by_str:
|
|
85
85
|
return str(by_str)
|
|
86
|
-
|
|
87
|
-
if env_rpc:
|
|
88
|
-
return env_rpc
|
|
89
|
-
raise ValueError("RPC URL not provided. Set strategy.rpc_urls or env RPC_URL.")
|
|
86
|
+
raise ValueError("RPC URL not provided. Set strategy.rpc_urls in config.json.")
|
|
90
87
|
|
|
91
88
|
|
|
92
89
|
async def get_next_nonce(
|
|
@@ -119,7 +116,7 @@ def resolve_private_key_for_from_address(
|
|
|
119
116
|
from_address: str, config: dict[str, Any]
|
|
120
117
|
) -> str | None:
|
|
121
118
|
"""
|
|
122
|
-
Resolve private key for the given address from config
|
|
119
|
+
Resolve private key for the given address from config.
|
|
123
120
|
|
|
124
121
|
Args:
|
|
125
122
|
from_address: Address to resolve private key for
|
|
@@ -159,14 +156,12 @@ def resolve_private_key_for_from_address(
|
|
|
159
156
|
logger.debug("Error resolving addresses from wallet config: %s", e)
|
|
160
157
|
|
|
161
158
|
if main_addr and from_addr_norm == (main_addr or "").lower():
|
|
162
|
-
return main_pk
|
|
159
|
+
return main_pk
|
|
163
160
|
if strategy_addr and from_addr_norm == (strategy_addr or "").lower():
|
|
164
|
-
return
|
|
165
|
-
strategy_pk or os.getenv("PRIVATE_KEY_STRATEGY") or os.getenv("PRIVATE_KEY")
|
|
166
|
-
)
|
|
161
|
+
return strategy_pk
|
|
167
162
|
|
|
168
|
-
#
|
|
169
|
-
return
|
|
163
|
+
# No fallback - private keys must be in config or wallets.json
|
|
164
|
+
return None
|
|
170
165
|
|
|
171
166
|
|
|
172
167
|
async def _get_abi(chain_id: int, address: str) -> str | None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wallet Abstraction Layer
|
|
2
2
|
|
|
3
|
-
Wayfinder strategies interact with blockchains through a single abstraction: the `EvmTxn` interface defined in `wayfinder_paths/core/services/base.py`. The default implementation (`LocalEvmTxn`) signs transactions with private keys pulled from config
|
|
3
|
+
Wayfinder strategies interact with blockchains through a single abstraction: the `EvmTxn` interface defined in `wayfinder_paths/core/services/base.py`. The default implementation (`LocalEvmTxn`) signs transactions with private keys pulled from config.json or wallets.json, while `WalletManager` resolves which provider to use at runtime.
|
|
4
4
|
|
|
5
5
|
## Pieces
|
|
6
6
|
|
|
@@ -46,9 +46,6 @@ class PrivyWallet(EvmTxn):
|
|
|
46
46
|
async def read_erc20_allowance(self, chain_id: int, token_address: str, owner_address: str, spender_address: str) -> tuple[bool, Any]:
|
|
47
47
|
...
|
|
48
48
|
|
|
49
|
-
async def approve_token(...):
|
|
50
|
-
...
|
|
51
|
-
|
|
52
49
|
async def broadcast_transaction(...):
|
|
53
50
|
...
|
|
54
51
|
|
|
@@ -76,8 +73,8 @@ web3_service = DefaultWeb3Service(config, wallet_provider=custom_wallet)
|
|
|
76
73
|
{
|
|
77
74
|
"strategy": {
|
|
78
75
|
"wallet_type": "local",
|
|
79
|
-
"main_wallet": {"address": "0x...", "wallet_type": "local"},
|
|
80
|
-
"strategy_wallet": {"address": "0x..."}
|
|
76
|
+
"main_wallet": { "address": "0x...", "wallet_type": "local" },
|
|
77
|
+
"strategy_wallet": { "address": "0x..." }
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
80
|
```
|