wayfinder-paths 0.1.10__py3-none-any.whl → 0.1.13__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.

Files changed (49) hide show
  1. wayfinder_paths/adapters/balance_adapter/adapter.py +3 -7
  2. wayfinder_paths/adapters/brap_adapter/adapter.py +10 -13
  3. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +6 -9
  4. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +1 -1
  5. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +44 -5
  6. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +104 -0
  7. wayfinder_paths/adapters/moonwell_adapter/adapter.py +0 -3
  8. wayfinder_paths/adapters/pool_adapter/README.md +4 -19
  9. wayfinder_paths/adapters/pool_adapter/adapter.py +4 -29
  10. wayfinder_paths/adapters/pool_adapter/examples.json +6 -7
  11. wayfinder_paths/adapters/pool_adapter/test_adapter.py +8 -8
  12. wayfinder_paths/core/clients/AuthClient.py +2 -2
  13. wayfinder_paths/core/clients/BRAPClient.py +2 -2
  14. wayfinder_paths/core/clients/HyperlendClient.py +2 -2
  15. wayfinder_paths/core/clients/PoolClient.py +18 -54
  16. wayfinder_paths/core/clients/TokenClient.py +3 -3
  17. wayfinder_paths/core/clients/WalletClient.py +2 -2
  18. wayfinder_paths/core/clients/WayfinderClient.py +9 -10
  19. wayfinder_paths/core/clients/protocols.py +1 -7
  20. wayfinder_paths/core/config.py +60 -224
  21. wayfinder_paths/core/services/base.py +0 -55
  22. wayfinder_paths/core/services/local_evm_txn.py +37 -134
  23. wayfinder_paths/core/strategies/Strategy.py +3 -3
  24. wayfinder_paths/core/strategies/descriptors.py +7 -0
  25. wayfinder_paths/core/utils/evm_helpers.py +5 -28
  26. wayfinder_paths/core/utils/wallets.py +12 -19
  27. wayfinder_paths/core/wallets/README.md +1 -1
  28. wayfinder_paths/run_strategy.py +10 -8
  29. wayfinder_paths/scripts/create_strategy.py +5 -5
  30. wayfinder_paths/scripts/make_wallets.py +5 -5
  31. wayfinder_paths/scripts/run_strategy.py +3 -3
  32. wayfinder_paths/strategies/basis_trading_strategy/snapshot_mixin.py +1 -1
  33. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +196 -515
  34. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +228 -11
  35. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +2 -2
  36. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +1 -0
  37. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +1 -1
  38. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +8 -7
  39. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +2 -2
  40. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +25 -25
  41. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +28 -9
  42. wayfinder_paths/templates/adapter/README.md +1 -1
  43. {wayfinder_paths-0.1.10.dist-info → wayfinder_paths-0.1.13.dist-info}/METADATA +15 -18
  44. {wayfinder_paths-0.1.10.dist-info → wayfinder_paths-0.1.13.dist-info}/RECORD +46 -48
  45. wayfinder_paths/CONFIG_GUIDE.md +0 -390
  46. wayfinder_paths/config.example.json +0 -22
  47. wayfinder_paths/core/settings.py +0 -61
  48. {wayfinder_paths-0.1.10.dist-info → wayfinder_paths-0.1.13.dist-info}/LICENSE +0 -0
  49. {wayfinder_paths-0.1.10.dist-info → wayfinder_paths-0.1.13.dist-info}/WHEEL +0 -0
@@ -4,16 +4,11 @@ from typing import Any
4
4
  from eth_account import Account
5
5
  from eth_utils import to_checksum_address
6
6
  from loguru import logger
7
- from web3 import AsyncHTTPProvider, AsyncWeb3, Web3
7
+ from web3 import AsyncHTTPProvider, AsyncWeb3
8
+ from web3.middleware import ExtraDataToPOAMiddleware
9
+ from web3.module import Module
8
10
 
9
- from wayfinder_paths.core.constants import (
10
- ZERO_ADDRESS,
11
- )
12
11
  from wayfinder_paths.core.constants.base import DEFAULT_TRANSACTION_TIMEOUT
13
- from wayfinder_paths.core.constants.erc20_abi import (
14
- ERC20_APPROVAL_ABI,
15
- ERC20_MINIMAL_ABI,
16
- )
17
12
  from wayfinder_paths.core.services.base import EvmTxn
18
13
  from wayfinder_paths.core.utils.evm_helpers import (
19
14
  resolve_private_key_for_from_address,
@@ -27,6 +22,7 @@ GAS_LIMIT_BUFFER_MULTIPLIER = 1.5
27
22
 
28
23
  # Chains that don't support EIP-1559 (London) and need legacy gas pricing
29
24
  PRE_LONDON_GAS_CHAIN_IDS: set[int] = {56, 42161}
25
+ POA_MIDDLEWARE_CHAIN_IDS: set = {56, 137, 43114}
30
26
 
31
27
 
32
28
  def _looks_like_revert_error(error: Any) -> bool:
@@ -47,12 +43,23 @@ def _looks_like_revert_error(error: Any) -> bool:
47
43
  )
48
44
 
49
45
 
46
+ class HyperModule(Module):
47
+ def __init__(self, w3):
48
+ super().__init__(w3)
49
+
50
+ async def big_block_gas_price(self):
51
+ big_block_gas_price = await self.w3.manager.coro_request(
52
+ "eth_bigBlockGasPrice", []
53
+ )
54
+ return int(big_block_gas_price, 16)
55
+
56
+
50
57
  class LocalEvmTxn(EvmTxn):
51
58
  """
52
- Local wallet provider using private keys stored in config.json or wallets.json.
59
+ Local wallet provider using private keys stored in config.json or config.json.
53
60
 
54
61
  This provider implements the current default behavior:
55
- - Resolves private keys from config.json or wallets.json
62
+ - Resolves private keys from config.json or config.json
56
63
  - Signs transactions using eth_account
57
64
  - Broadcasts transactions via RPC
58
65
  """
@@ -60,104 +67,15 @@ class LocalEvmTxn(EvmTxn):
60
67
  def __init__(self, config: dict[str, Any] | None = None):
61
68
  self.config = config or {}
62
69
  self.logger = logger.bind(provider="LocalWalletProvider")
63
- # Cache web3 instances per chain to avoid load balancer inconsistency
64
- self._web3_cache: dict[int, AsyncWeb3] = {}
65
70
 
66
71
  def get_web3(self, chain_id: int) -> AsyncWeb3:
67
- # Reuse cached instance to ensure consistent RPC node for reads after writes
68
- if chain_id in self._web3_cache:
69
- return self._web3_cache[chain_id]
70
72
  rpc_url = self._resolve_rpc_url(chain_id)
71
- w3 = AsyncWeb3(AsyncHTTPProvider(rpc_url))
72
- self._web3_cache[chain_id] = w3
73
- return w3
74
-
75
- async def get_balance(
76
- self,
77
- address: str,
78
- token_address: str | None,
79
- chain_id: int,
80
- block_identifier: int | str | None = None,
81
- ) -> tuple[bool, Any]:
82
- """
83
- Get balance for an address at a specific block.
84
-
85
- Args:
86
- address: Address to query balance for
87
- token_address: ERC20 token address, or None for native token
88
- chain_id: Chain ID
89
- block_identifier: Block to query at. Can be:
90
- - int: specific block number (for pinning to tx block)
91
- - "safe": OP Stack safe block (data posted to L1)
92
- - "finalized": fully finalized block
93
- - None/"latest": current head (default, but avoid after txs)
94
-
95
- Returns:
96
- Tuple of (success, balance_integer_or_error_message)
97
- """
98
- w3 = self.get_web3(chain_id)
99
- try:
100
- checksum_addr = to_checksum_address(address)
101
- block_id = block_identifier if block_identifier is not None else "latest"
102
-
103
- if not token_address or token_address.lower() == ZERO_ADDRESS:
104
- balance = await w3.eth.get_balance(
105
- checksum_addr, block_identifier=block_id
106
- )
107
- return (True, int(balance))
108
-
109
- token_checksum = to_checksum_address(token_address)
110
- contract = w3.eth.contract(address=token_checksum, abi=ERC20_MINIMAL_ABI)
111
- balance = await contract.functions.balanceOf(checksum_addr).call(
112
- block_identifier=block_id
113
- )
114
- return (True, int(balance))
115
-
116
- except Exception as exc: # noqa: BLE001
117
- self.logger.error(f"Failed to get balance: {exc}")
118
- return (False, f"Balance query failed: {exc}")
119
- finally:
120
- await self._close_web3(w3)
121
-
122
- async def approve_token(
123
- self,
124
- token_address: str,
125
- spender: str,
126
- amount: int,
127
- from_address: str,
128
- chain_id: int,
129
- wait_for_receipt: bool = True,
130
- timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
131
- ) -> tuple[bool, Any]:
132
- try:
133
- token_checksum = to_checksum_address(token_address)
134
- spender_checksum = to_checksum_address(spender)
135
- from_checksum = to_checksum_address(from_address)
136
- amount_int = int(amount)
137
-
138
- w3_sync = Web3()
139
- contract = w3_sync.eth.contract(
140
- address=token_checksum, abi=ERC20_APPROVAL_ABI
141
- )
142
- transaction_data = contract.encode_abi(
143
- "approve", args=[spender_checksum, amount_int]
144
- )
145
-
146
- approve_txn = {
147
- "from": from_checksum,
148
- "chainId": int(chain_id),
149
- "to": token_checksum,
150
- "data": transaction_data,
151
- }
152
-
153
- return await self.broadcast_transaction(
154
- approve_txn,
155
- wait_for_receipt=wait_for_receipt,
156
- timeout=timeout,
157
- )
158
- except Exception as exc: # noqa: BLE001
159
- self.logger.error(f"ERC20 approval failed: {exc}")
160
- return (False, f"ERC20 approval failed: {exc}")
73
+ web3 = AsyncWeb3(AsyncHTTPProvider(rpc_url))
74
+ if chain_id in POA_MIDDLEWARE_CHAIN_IDS:
75
+ web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
76
+ if chain_id == 999:
77
+ web3.attach_modules({"hype": (HyperModule)})
78
+ return web3
161
79
 
162
80
  def _validate_transaction(self, transaction: dict[str, Any]) -> dict[str, Any]:
163
81
  tx = dict(transaction)
@@ -264,29 +182,29 @@ class LocalEvmTxn(EvmTxn):
264
182
  *,
265
183
  wait_for_receipt: bool = True,
266
184
  timeout: int = DEFAULT_TRANSACTION_TIMEOUT,
267
- confirmations: int = 0,
185
+ confirmations: int = 1,
268
186
  ) -> tuple[bool, Any]:
269
187
  try:
270
188
  chain_id = transaction["chainId"]
271
189
  from_address = transaction["from"]
272
190
 
273
- w3 = self.get_web3(chain_id)
191
+ web3 = self.get_web3(chain_id)
274
192
  try:
275
193
  transaction = self._validate_transaction(transaction)
276
- transaction = await self._nonce_transaction(transaction, w3)
277
- transaction = await self._gas_limit_transaction(transaction, w3)
194
+ transaction = await self._nonce_transaction(transaction, web3)
195
+ transaction = await self._gas_limit_transaction(transaction, web3)
278
196
  transaction = await self._gas_price_transaction(
279
- transaction, chain_id, w3
197
+ transaction, chain_id, web3
280
198
  )
281
199
 
282
200
  signed_tx = self._sign_transaction(transaction, from_address)
283
201
 
284
- tx_hash = await w3.eth.send_raw_transaction(signed_tx)
202
+ tx_hash = await web3.eth.send_raw_transaction(signed_tx)
285
203
  tx_hash_hex = tx_hash.hex()
286
204
 
287
205
  result: dict[str, Any] = {"tx_hash": tx_hash_hex}
288
206
  if wait_for_receipt:
289
- receipt = await w3.eth.wait_for_transaction_receipt(
207
+ receipt = await web3.eth.wait_for_transaction_receipt(
290
208
  tx_hash, timeout=timeout
291
209
  )
292
210
  result["receipt"] = self._format_receipt(receipt)
@@ -299,32 +217,19 @@ class LocalEvmTxn(EvmTxn):
299
217
  False,
300
218
  f"Transaction reverted (status={receipt_status}): {tx_hash_hex}",
301
219
  )
302
- # Check if transaction reverted (status=0)
303
- # Handle both dict-like and attribute access for web3.py receipts
304
- receipt_status = (
305
- receipt.get("status")
306
- if hasattr(receipt, "get")
307
- else getattr(receipt, "status", None)
308
- )
309
- self.logger.debug(
310
- f"Transaction {tx_hash_hex} receipt status: {receipt_status} (type: {type(receipt_status).__name__})"
311
- )
312
- if receipt_status == 0:
313
- self.logger.error(f"Transaction reverted: {tx_hash_hex}")
314
- return (False, f"Transaction reverted: {tx_hash_hex}")
315
220
 
316
221
  # Wait for additional confirmations if requested
317
222
  if confirmations > 0:
318
223
  tx_block = result["receipt"].get("blockNumber")
319
224
  if tx_block:
320
225
  await self._wait_for_confirmations(
321
- w3, tx_block, confirmations
226
+ web3, tx_block, confirmations
322
227
  )
323
228
 
324
229
  return (True, result)
325
230
 
326
231
  finally:
327
- await self._close_web3(w3)
232
+ await self._close_web3(web3)
328
233
 
329
234
  except Exception as exc: # noqa: BLE001
330
235
  self.logger.error(f"Transaction broadcast failed: {exc}")
@@ -362,14 +267,12 @@ class LocalEvmTxn(EvmTxn):
362
267
  def _resolve_rpc_url(self, chain_id: int) -> str:
363
268
  return resolve_rpc_url(chain_id, self.config or {}, None)
364
269
 
365
- async def _close_web3(self, w3: AsyncWeb3) -> None:
366
- # Don't close cached connections - we want to reuse them for consistency
367
- if w3 in self._web3_cache.values():
368
- return
270
+ async def _close_web3(self, web3: AsyncWeb3) -> None:
369
271
  try:
370
- await w3.provider.session.close()
371
- except Exception: # noqa: BLE001
372
- pass
272
+ if hasattr(web3.provider, "disconnect"):
273
+ await web3.provider.disconnect()
274
+ except Exception as e: # noqa: BLE001
275
+ self.logger.debug(f"Error disconnecting provider: {e}")
373
276
 
374
277
  async def _wait_for_confirmations(
375
278
  self, w3: AsyncWeb3, tx_block: int, confirmations: int
@@ -152,21 +152,21 @@ class Strategy(ABC):
152
152
 
153
153
  @staticmethod
154
154
  async def policies() -> list[str]:
155
- """Return policy strings for this strategy (Django-compatible)."""
155
+ """Return policy strings for this strategy."""
156
156
  raise NotImplementedError
157
157
 
158
158
  @abstractmethod
159
159
  async def _status(self) -> StatusDict:
160
160
  """
161
161
  Return status payload. Subclasses should implement this.
162
- Should include Django-compatible keys (portfolio_value, net_deposit, strategy_status).
162
+ Should include keys (portfolio_value, net_deposit, strategy_status).
163
163
  Backward-compatible keys (active_amount, total_earned) may also be included.
164
164
  """
165
165
  pass
166
166
 
167
167
  async def status(self) -> StatusDict:
168
168
  """
169
- Wrapper to compute and return strategy status. In Django, this also snapshots.
169
+ Wrapper to compute and return strategy status and record a snapshot.
170
170
  Here we simply delegate to _status for compatibility.
171
171
  """
172
172
 
@@ -46,12 +46,19 @@ DEFAULT_TOKEN_REWARDS = [
46
46
  },
47
47
  ]
48
48
 
49
+ DEFAULT_FEE_DESCRIPTION = """Wayfinder deducts a 10% performance fee on profits generated by this vault. Fees are collected from the assets in the vault.
50
+
51
+ If fees remain unpaid, Wayfinder may pause automated management of this vault."""
52
+
49
53
 
50
54
  class StratDescriptor(BaseModel):
51
55
  description: str
52
56
 
53
57
  summary: str
54
58
 
59
+ risk_description: str
60
+ fee_description: str = DEFAULT_FEE_DESCRIPTION
61
+
55
62
  gas_token_symbol: str
56
63
  gas_token_id: str
57
64
  deposit_token_id: str
@@ -11,7 +11,6 @@ from pathlib import Path
11
11
  from typing import Any
12
12
 
13
13
  from loguru import logger
14
- from web3 import AsyncHTTPProvider, AsyncWeb3
15
14
 
16
15
  from wayfinder_paths.core.constants.base import CHAIN_CODE_TO_ID
17
16
 
@@ -79,39 +78,17 @@ def resolve_rpc_url(
79
78
  if chain_id is not None and isinstance(mapping, dict):
80
79
  by_int = mapping.get(chain_id)
81
80
  if by_int:
81
+ if isinstance(by_int, list):
82
+ return str(by_int[0])
82
83
  return str(by_int)
83
84
  by_str = mapping.get(str(chain_id))
84
85
  if by_str:
86
+ if isinstance(by_str, list):
87
+ return str(by_str[0])
85
88
  return str(by_str)
86
89
  raise ValueError("RPC URL not provided. Set strategy.rpc_urls in config.json.")
87
90
 
88
91
 
89
- async def get_next_nonce(
90
- from_address: str, rpc_url: str, use_latest: bool = False
91
- ) -> int:
92
- """
93
- Get the next nonce for the given address.
94
-
95
- Args:
96
- from_address: Address to get nonce for
97
- rpc_url: RPC URL to connect to
98
- use_latest: If True, use 'latest' block instead of 'pending'
99
-
100
- Returns:
101
- Next nonce as integer
102
- """
103
- w3 = AsyncWeb3(AsyncHTTPProvider(rpc_url))
104
- try:
105
- if use_latest:
106
- return await w3.eth.get_transaction_count(from_address, "latest")
107
- return await w3.eth.get_transaction_count(from_address)
108
- finally:
109
- try:
110
- await w3.provider.session.close()
111
- except Exception:
112
- pass
113
-
114
-
115
92
  def resolve_private_key_for_from_address(
116
93
  from_address: str, config: dict[str, Any]
117
94
  ) -> str | None:
@@ -160,7 +137,7 @@ def resolve_private_key_for_from_address(
160
137
  if strategy_addr and from_addr_norm == (strategy_addr or "").lower():
161
138
  return strategy_pk
162
139
 
163
- # No fallback - private keys must be in config or wallets.json
140
+ # No fallback - private keys must be in config or config.json
164
141
  return None
165
142
 
166
143
 
@@ -6,11 +6,7 @@ from eth_account import Account
6
6
 
7
7
 
8
8
  def make_random_wallet() -> dict[str, str]:
9
- """Generate a new random wallet.
10
-
11
- Returns a mapping with keys: "address" and "private_key_hex" (0x-prefixed).
12
- """
13
- acct = Account.create() # uses os.urandom
9
+ acct = Account.create()
14
10
  return {
15
11
  "address": acct.address,
16
12
  "private_key_hex": acct.key.hex(),
@@ -22,33 +18,31 @@ def _load_existing_wallets(file_path: Path) -> list[dict[str, Any]]:
22
18
  return []
23
19
  try:
24
20
  parsed = json.loads(file_path.read_text())
25
- if isinstance(parsed, list):
26
- return parsed
27
21
  if isinstance(parsed, dict):
28
22
  wallets = parsed.get("wallets")
29
23
  if isinstance(wallets, list):
30
24
  return wallets
31
25
  return []
32
26
  except Exception:
33
- # If the file is malformed, start fresh rather than raising.
34
27
  return []
35
28
 
36
29
 
37
30
  def _save_wallets(file_path: Path, wallets: list[dict[str, Any]]) -> None:
38
- # Ensure stable ordering by address for readability
31
+ config = {}
32
+ if file_path.exists():
33
+ try:
34
+ config = json.loads(file_path.read_text())
35
+ except Exception:
36
+ pass
37
+
39
38
  sorted_wallets = sorted(wallets, key=lambda w: w.get("address", ""))
40
- file_path.write_text(json.dumps(sorted_wallets, indent=2))
39
+ config["wallets"] = sorted_wallets
40
+ file_path.write_text(json.dumps(config, indent=2))
41
41
 
42
42
 
43
43
  def write_wallet_to_json(
44
- wallet: dict[str, str], out_dir: str | Path = ".", filename: str = "wallets.json"
44
+ wallet: dict[str, str], out_dir: str | Path = ".", filename: str = "config.json"
45
45
  ) -> Path:
46
- """Create or update a wallets.json with the provided wallet.
47
-
48
- - Ensures the output directory exists.
49
- - Merges with existing entries keyed by address (updates if present, appends otherwise).
50
- - Writes a pretty-printed JSON list of wallet objects.
51
- """
52
46
  out_dir_path = Path(out_dir)
53
47
  out_dir_path.mkdir(parents=True, exist_ok=True)
54
48
  file_path = out_dir_path / filename
@@ -71,7 +65,6 @@ def write_wallet_to_json(
71
65
 
72
66
 
73
67
  def load_wallets(
74
- out_dir: str | Path = ".", filename: str = "wallets.json"
68
+ out_dir: str | Path = ".", filename: str = "config.json"
75
69
  ) -> list[dict[str, Any]]:
76
- """Public helper to read wallets.json as a list of wallet dicts."""
77
70
  return _load_existing_wallets(Path(out_dir) / filename)
@@ -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.json or wallets.json, while `WalletManager` resolves which provider to use at runtime.
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 config.json, while `WalletManager` resolves which provider to use at runtime.
4
4
 
5
5
  ## Pieces
6
6
 
@@ -135,14 +135,16 @@ async def run_strategy(
135
135
  # Load configuration with strategy name for wallet lookup
136
136
  logger.debug(f"Config path provided: {config_path}")
137
137
  config = load_config(config_path, strategy_name=strategy_name)
138
- logger.debug(
139
- "Loaded config: creds=%s wallets(main=%s strategy=%s)",
138
+ creds = (
140
139
  "yes"
141
140
  if (config.user.username and config.user.password)
142
141
  or config.user.refresh_token
143
- else "no",
144
- (config.user.main_wallet_address or "none"),
145
- (config.user.strategy_wallet_address or "none"),
142
+ else "no"
143
+ )
144
+ main_wallet = config.user.main_wallet_address or "none"
145
+ strategy_wallet = config.user.strategy_wallet_address or "none"
146
+ logger.debug(
147
+ f"Loaded config: creds={creds} wallets(main={main_wallet} strategy={strategy_wallet})"
146
148
  )
147
149
 
148
150
  # Load strategy with the enriched config
@@ -157,13 +159,13 @@ async def run_strategy(
157
159
 
158
160
  # Setup strategy job
159
161
  logger.info("Setting up strategy job...")
160
- logger.debug(
161
- "Auth mode: %s",
162
+ auth_mode = (
162
163
  "credentials"
163
164
  if (config.user.username and config.user.password)
164
165
  or config.user.refresh_token
165
- else "missing",
166
+ else "missing"
166
167
  )
168
+ logger.debug(f"Auth mode: {auth_mode}")
167
169
  await strategy_job.setup()
168
170
 
169
171
  # Execute action
@@ -62,8 +62,8 @@ def main():
62
62
  parser.add_argument(
63
63
  "--wallets-file",
64
64
  type=Path,
65
- default=Path(__file__).parent.parent.parent / "wallets.json",
66
- help="Path to wallets.json file",
65
+ default=Path(__file__).parent.parent.parent / "config.json",
66
+ help="Path to config.json file",
67
67
  )
68
68
  parser.add_argument(
69
69
  "--override",
@@ -121,9 +121,9 @@ def main():
121
121
  print(f" Updated strategy.py with class name: {class_name}")
122
122
 
123
123
  # Generate wallet with label matching directory name (strategy identifier)
124
- # If wallets.json doesn't exist, create it with a main wallet first
124
+ # If config.json doesn't exist, create it with a main wallet first
125
125
  if not args.wallets_file.exists():
126
- print(" Creating new wallets.json with main wallet...")
126
+ print(" Creating new config.json with main wallet...")
127
127
  main_wallet = make_random_wallet()
128
128
  main_wallet["label"] = "main"
129
129
  write_wallet_to_json(
@@ -133,7 +133,7 @@ def main():
133
133
  )
134
134
  print(f" Generated main wallet: {main_wallet['address']}")
135
135
 
136
- # Generate strategy wallet (will append to existing wallets.json)
136
+ # Generate strategy wallet (will append to existing config.json)
137
137
  wallet = make_random_wallet()
138
138
  wallet["label"] = dir_name
139
139
  write_wallet_to_json(
@@ -27,7 +27,7 @@ def main():
27
27
  "--out-dir",
28
28
  type=Path,
29
29
  default=Path("."),
30
- help="Output directory for wallets.json (and keystore files)",
30
+ help="Output directory for config.json (and keystore files)",
31
31
  )
32
32
  parser.add_argument(
33
33
  "--keystore-password",
@@ -55,7 +55,7 @@ def main():
55
55
  args.out_dir.mkdir(parents=True, exist_ok=True)
56
56
 
57
57
  # Load existing wallets
58
- existing = load_wallets(args.out_dir, "wallets.json")
58
+ existing = load_wallets(args.out_dir, "config.json")
59
59
  has_main = any(w.get("label") in ("main", "default") for w in existing)
60
60
 
61
61
  rows: list[dict[str, str]] = []
@@ -72,7 +72,7 @@ def main():
72
72
  w["label"] = args.label
73
73
  rows.append(w)
74
74
  print(f"[{index}] {w['address']} (label: {args.label})")
75
- write_wallet_to_json(w, out_dir=args.out_dir, filename="wallets.json")
75
+ write_wallet_to_json(w, out_dir=args.out_dir, filename="config.json")
76
76
  if args.keystore_password:
77
77
  ks = to_keystore_json(w["private_key_hex"], args.keystore_password)
78
78
  ks_path = args.out_dir / f"keystore_{w['address']}.json"
@@ -86,7 +86,7 @@ def main():
86
86
  rows.append(main_w)
87
87
  print(f"[{index}] {main_w['address']} (main)")
88
88
  write_wallet_to_json(
89
- main_w, out_dir=args.out_dir, filename="wallets.json"
89
+ main_w, out_dir=args.out_dir, filename="config.json"
90
90
  )
91
91
  if args.keystore_password:
92
92
  ks = to_keystore_json(
@@ -133,7 +133,7 @@ def main():
133
133
  rows.append(w)
134
134
  print(f"[{index}] {w['address']} (label: temporary_{next_temp_num})")
135
135
 
136
- write_wallet_to_json(w, out_dir=args.out_dir, filename="wallets.json")
136
+ write_wallet_to_json(w, out_dir=args.out_dir, filename="config.json")
137
137
  if args.keystore_password:
138
138
  ks = to_keystore_json(w["private_key_hex"], args.keystore_password)
139
139
  ks_path = args.out_dir / f"keystore_{w['address']}.json"
@@ -27,7 +27,7 @@ def _find_wallet(wallets: list[dict[str, Any]], label: str) -> dict[str, Any]:
27
27
  for w in wallets:
28
28
  if w.get("label") == label:
29
29
  return w
30
- raise ValueError(f"Wallet label not found in wallets.json: {label}")
30
+ raise ValueError(f"Wallet label not found in config.json: {label}")
31
31
 
32
32
 
33
33
  def _get_strategy_class(strategy: str):
@@ -58,7 +58,7 @@ def _get_strategy_class(strategy: str):
58
58
  async def _run(args: argparse.Namespace) -> int:
59
59
  repo_root = Path(__file__).resolve().parents[2]
60
60
  wallets_path = (
61
- Path(args.wallets).resolve() if args.wallets else repo_root / "wallets.json"
61
+ Path(args.wallets).resolve() if args.wallets else repo_root / "config.json"
62
62
  )
63
63
  config_path = (
64
64
  Path(args.config).resolve() if args.config else repo_root / "config.json"
@@ -123,7 +123,7 @@ def main() -> int:
123
123
  ],
124
124
  )
125
125
  p.add_argument(
126
- "--wallets", default=None, help="Path to wallets.json (default: repo root)"
126
+ "--wallets", default=None, help="Path to config.json (default: repo root)"
127
127
  )
128
128
  p.add_argument(
129
129
  "--config", default=None, help="Path to config.json (default: repo root)"
@@ -38,7 +38,7 @@ class BasisSnapshotMixin:
38
38
  entry_cost_usd: float,
39
39
  exit_cost_usd: float,
40
40
  ) -> dict[str, Any]:
41
- """Build the `safe[horizon]` entry matching Django output shape."""
41
+ """Build the `safe[horizon]` entry matching the expected output shape."""
42
42
  L = max(1, int(leverage))
43
43
 
44
44
  depth_checks = depth_checks or {}