wayfinder-paths 0.1.22__py3-none-any.whl → 0.1.24__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 (156) hide show
  1. wayfinder_paths/__init__.py +0 -4
  2. wayfinder_paths/adapters/balance_adapter/README.md +0 -1
  3. wayfinder_paths/adapters/balance_adapter/adapter.py +313 -167
  4. wayfinder_paths/adapters/balance_adapter/manifest.yaml +8 -0
  5. wayfinder_paths/adapters/balance_adapter/test_adapter.py +41 -124
  6. wayfinder_paths/adapters/boros_adapter/__init__.py +17 -0
  7. wayfinder_paths/adapters/boros_adapter/adapter.py +1574 -0
  8. wayfinder_paths/adapters/boros_adapter/client.py +476 -0
  9. wayfinder_paths/adapters/boros_adapter/manifest.yaml +10 -0
  10. wayfinder_paths/adapters/boros_adapter/parsers.py +88 -0
  11. wayfinder_paths/adapters/boros_adapter/test_adapter.py +460 -0
  12. wayfinder_paths/adapters/boros_adapter/test_golden.py +156 -0
  13. wayfinder_paths/adapters/boros_adapter/types.py +70 -0
  14. wayfinder_paths/adapters/boros_adapter/utils.py +85 -0
  15. wayfinder_paths/adapters/brap_adapter/README.md +22 -75
  16. wayfinder_paths/adapters/brap_adapter/adapter.py +187 -576
  17. wayfinder_paths/adapters/brap_adapter/examples.json +21 -140
  18. wayfinder_paths/adapters/brap_adapter/manifest.yaml +9 -0
  19. wayfinder_paths/adapters/brap_adapter/test_adapter.py +6 -234
  20. wayfinder_paths/adapters/hyperlend_adapter/adapter.py +180 -92
  21. wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +9 -0
  22. wayfinder_paths/adapters/hyperlend_adapter/test_adapter.py +82 -14
  23. wayfinder_paths/adapters/hyperliquid_adapter/__init__.py +2 -9
  24. wayfinder_paths/adapters/hyperliquid_adapter/adapter.py +586 -61
  25. wayfinder_paths/adapters/hyperliquid_adapter/executor.py +47 -68
  26. wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +14 -0
  27. wayfinder_paths/adapters/hyperliquid_adapter/paired_filler.py +2 -3
  28. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter.py +17 -21
  29. wayfinder_paths/adapters/hyperliquid_adapter/test_adapter_live.py +3 -6
  30. wayfinder_paths/adapters/hyperliquid_adapter/test_executor.py +4 -8
  31. wayfinder_paths/adapters/hyperliquid_adapter/test_utils.py +2 -2
  32. wayfinder_paths/adapters/ledger_adapter/README.md +4 -1
  33. wayfinder_paths/adapters/ledger_adapter/adapter.py +3 -3
  34. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +7 -0
  35. wayfinder_paths/adapters/ledger_adapter/test_adapter.py +1 -2
  36. wayfinder_paths/adapters/moonwell_adapter/adapter.py +649 -547
  37. wayfinder_paths/adapters/moonwell_adapter/manifest.yaml +14 -0
  38. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +160 -239
  39. wayfinder_paths/adapters/multicall_adapter/__init__.py +7 -0
  40. wayfinder_paths/adapters/multicall_adapter/adapter.py +166 -0
  41. wayfinder_paths/adapters/multicall_adapter/manifest.yaml +5 -0
  42. wayfinder_paths/adapters/multicall_adapter/test_adapter.py +97 -0
  43. wayfinder_paths/adapters/pendle_adapter/README.md +102 -0
  44. wayfinder_paths/adapters/pendle_adapter/__init__.py +7 -0
  45. wayfinder_paths/adapters/pendle_adapter/adapter.py +1992 -0
  46. wayfinder_paths/adapters/pendle_adapter/examples.json +11 -0
  47. wayfinder_paths/adapters/pendle_adapter/manifest.yaml +21 -0
  48. wayfinder_paths/adapters/pendle_adapter/test_adapter.py +666 -0
  49. wayfinder_paths/adapters/pool_adapter/manifest.yaml +6 -0
  50. wayfinder_paths/adapters/token_adapter/adapter.py +14 -0
  51. wayfinder_paths/adapters/token_adapter/examples.json +0 -4
  52. wayfinder_paths/adapters/token_adapter/manifest.yaml +7 -0
  53. wayfinder_paths/conftest.py +24 -17
  54. wayfinder_paths/core/__init__.py +0 -3
  55. wayfinder_paths/core/adapters/BaseAdapter.py +0 -25
  56. wayfinder_paths/core/adapters/models.py +17 -7
  57. wayfinder_paths/core/clients/BRAPClient.py +4 -1
  58. wayfinder_paths/core/clients/ClientManager.py +0 -7
  59. wayfinder_paths/core/clients/LedgerClient.py +196 -172
  60. wayfinder_paths/core/clients/TokenClient.py +47 -1
  61. wayfinder_paths/core/clients/WayfinderClient.py +1 -3
  62. wayfinder_paths/core/clients/__init__.py +0 -5
  63. wayfinder_paths/core/clients/protocols.py +21 -35
  64. wayfinder_paths/core/clients/test_ledger_client.py +448 -0
  65. wayfinder_paths/core/config.py +10 -162
  66. wayfinder_paths/core/constants/__init__.py +73 -2
  67. wayfinder_paths/core/constants/base.py +8 -17
  68. wayfinder_paths/core/constants/chains.py +36 -0
  69. wayfinder_paths/core/constants/contracts.py +52 -0
  70. wayfinder_paths/core/constants/erc20_abi.py +0 -1
  71. wayfinder_paths/core/constants/hyperlend_abi.py +0 -4
  72. wayfinder_paths/core/constants/hyperliquid.py +16 -0
  73. wayfinder_paths/core/constants/moonwell_abi.py +0 -15
  74. wayfinder_paths/core/constants/tokens.py +9 -0
  75. wayfinder_paths/core/engine/manifest.py +66 -0
  76. wayfinder_paths/core/strategies/Strategy.py +0 -71
  77. wayfinder_paths/core/strategies/__init__.py +10 -1
  78. wayfinder_paths/core/strategies/opa_loop.py +167 -0
  79. wayfinder_paths/core/utils/evm_helpers.py +5 -15
  80. wayfinder_paths/core/utils/test_transaction.py +289 -0
  81. wayfinder_paths/core/utils/tokens.py +28 -0
  82. wayfinder_paths/core/utils/transaction.py +57 -8
  83. wayfinder_paths/core/utils/web3.py +8 -3
  84. wayfinder_paths/mcp/__init__.py +5 -0
  85. wayfinder_paths/mcp/preview.py +185 -0
  86. wayfinder_paths/mcp/scripting.py +84 -0
  87. wayfinder_paths/mcp/server.py +52 -0
  88. wayfinder_paths/mcp/state/profile_store.py +195 -0
  89. wayfinder_paths/mcp/state/store.py +89 -0
  90. wayfinder_paths/mcp/test_scripting.py +267 -0
  91. wayfinder_paths/mcp/tools/__init__.py +0 -0
  92. wayfinder_paths/mcp/tools/balances.py +290 -0
  93. wayfinder_paths/mcp/tools/discovery.py +158 -0
  94. wayfinder_paths/mcp/tools/execute.py +770 -0
  95. wayfinder_paths/mcp/tools/hyperliquid.py +931 -0
  96. wayfinder_paths/mcp/tools/quotes.py +288 -0
  97. wayfinder_paths/mcp/tools/run_script.py +286 -0
  98. wayfinder_paths/mcp/tools/strategies.py +188 -0
  99. wayfinder_paths/mcp/tools/tokens.py +46 -0
  100. wayfinder_paths/mcp/tools/wallets.py +354 -0
  101. wayfinder_paths/mcp/utils.py +129 -0
  102. wayfinder_paths/policies/enso.py +1 -2
  103. wayfinder_paths/policies/hyper_evm.py +6 -3
  104. wayfinder_paths/policies/hyperlend.py +1 -2
  105. wayfinder_paths/policies/hyperliquid.py +1 -1
  106. wayfinder_paths/policies/lifi.py +18 -0
  107. wayfinder_paths/policies/moonwell.py +12 -7
  108. wayfinder_paths/policies/prjx.py +1 -3
  109. wayfinder_paths/policies/util.py +8 -2
  110. wayfinder_paths/run_strategy.py +97 -300
  111. wayfinder_paths/strategies/basis_trading_strategy/constants.py +3 -1
  112. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +47 -133
  113. wayfinder_paths/strategies/basis_trading_strategy/test_strategy.py +24 -53
  114. wayfinder_paths/strategies/boros_hype_strategy/__init__.py +3 -0
  115. wayfinder_paths/strategies/boros_hype_strategy/boros_ops_mixin.py +450 -0
  116. wayfinder_paths/strategies/boros_hype_strategy/constants.py +255 -0
  117. wayfinder_paths/strategies/boros_hype_strategy/examples.json +37 -0
  118. wayfinder_paths/strategies/boros_hype_strategy/hyperevm_ops_mixin.py +114 -0
  119. wayfinder_paths/strategies/boros_hype_strategy/hyperliquid_ops_mixin.py +642 -0
  120. wayfinder_paths/strategies/boros_hype_strategy/manifest.yaml +36 -0
  121. wayfinder_paths/strategies/boros_hype_strategy/planner.py +460 -0
  122. wayfinder_paths/strategies/boros_hype_strategy/risk_ops_mixin.py +886 -0
  123. wayfinder_paths/strategies/boros_hype_strategy/snapshot_mixin.py +494 -0
  124. wayfinder_paths/strategies/boros_hype_strategy/strategy.py +1194 -0
  125. wayfinder_paths/strategies/boros_hype_strategy/test_planner_golden.py +374 -0
  126. wayfinder_paths/{templates/strategy → strategies/boros_hype_strategy}/test_strategy.py +99 -63
  127. wayfinder_paths/strategies/boros_hype_strategy/types.py +365 -0
  128. wayfinder_paths/strategies/boros_hype_strategy/withdraw_mixin.py +997 -0
  129. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +15 -23
  130. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +27 -62
  131. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +84 -58
  132. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +5 -15
  133. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +69 -164
  134. wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +43 -76
  135. wayfinder_paths/tests/test_mcp_quote_swap.py +165 -0
  136. wayfinder_paths/tests/test_test_coverage.py +1 -4
  137. wayfinder_paths-0.1.24.dist-info/METADATA +378 -0
  138. wayfinder_paths-0.1.24.dist-info/RECORD +185 -0
  139. {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/WHEEL +1 -1
  140. wayfinder_paths/core/clients/WalletClient.py +0 -41
  141. wayfinder_paths/core/engine/StrategyJob.py +0 -110
  142. wayfinder_paths/core/services/test_local_evm_txn.py +0 -145
  143. wayfinder_paths/scripts/create_strategy.py +0 -139
  144. wayfinder_paths/scripts/make_wallets.py +0 -142
  145. wayfinder_paths/templates/adapter/README.md +0 -150
  146. wayfinder_paths/templates/adapter/adapter.py +0 -16
  147. wayfinder_paths/templates/adapter/examples.json +0 -8
  148. wayfinder_paths/templates/adapter/test_adapter.py +0 -30
  149. wayfinder_paths/templates/strategy/README.md +0 -186
  150. wayfinder_paths/templates/strategy/examples.json +0 -11
  151. wayfinder_paths/templates/strategy/strategy.py +0 -35
  152. wayfinder_paths/tests/test_smoke_manifest.py +0 -63
  153. wayfinder_paths-0.1.22.dist-info/METADATA +0 -355
  154. wayfinder_paths-0.1.22.dist-info/RECORD +0 -129
  155. /wayfinder_paths/{scripts → mcp/state}/__init__.py +0 -0
  156. {wayfinder_paths-0.1.22.dist-info → wayfinder_paths-0.1.24.dist-info}/LICENSE +0 -0
@@ -2,345 +2,142 @@
2
2
 
3
3
  import argparse
4
4
  import asyncio
5
+ import importlib
6
+ import inspect
5
7
  import json
6
8
  import sys
7
- from pathlib import Path
9
+ from typing import Any
8
10
 
9
11
  from loguru import logger
10
12
 
11
- from wayfinder_paths.core.config import StrategyJobConfig
12
- from wayfinder_paths.core.engine.StrategyJob import StrategyJob
13
+ from wayfinder_paths.core.config import CONFIG
14
+ from wayfinder_paths.core.strategies.Strategy import Strategy
13
15
  from wayfinder_paths.core.utils.evm_helpers import resolve_private_key_for_from_address
14
16
  from wayfinder_paths.core.utils.web3 import get_transaction_chain_id, web3_from_chain_id
15
17
 
16
18
 
17
- def load_strategy(
18
- strategy_name: str,
19
- *,
20
- config: StrategyJobConfig,
21
- ):
22
- strategies_dir = Path(__file__).parent / "strategies"
23
- strategy_dir = strategies_dir / strategy_name
19
+ def get_strategy_config(strategy_name: str) -> dict[str, Any]:
20
+ config = dict(CONFIG.get("strategy", {}))
21
+ wallets = {w["label"]: w for w in CONFIG.get("wallets", [])}
24
22
 
25
- if not strategy_dir.exists():
26
- # List available strategies for better error message
27
- available = []
28
- if strategies_dir.exists():
29
- for path in strategies_dir.iterdir():
30
- if path.is_dir() and (path / "strategy.py").exists():
31
- available.append(path.name)
32
- available_str = ", ".join(available) if available else "none"
33
- raise ValueError(
34
- f"Unknown strategy: {strategy_name}. Available strategies: {available_str}"
35
- )
36
-
37
- module_path = f"strategies.{strategy_name}.strategy"
38
- module = __import__(module_path, fromlist=[""])
39
-
40
- # Find the Strategy subclass in the module
41
- from wayfinder_paths.core.strategies.Strategy import Strategy
42
-
43
- strategy_class = None
44
- for attr_name in dir(module):
45
- attr = getattr(module, attr_name)
46
- if (
47
- isinstance(attr, type)
48
- and issubclass(attr, Strategy)
49
- and attr is not Strategy
50
- ):
51
- strategy_class = attr
52
- break
23
+ if "main_wallet" not in config and "main" in wallets:
24
+ config["main_wallet"] = {"address": wallets["main"]["address"]}
25
+ if "strategy_wallet" not in config and strategy_name in wallets:
26
+ config["strategy_wallet"] = {"address": wallets[strategy_name]["address"]}
53
27
 
54
- if strategy_class is None:
55
- raise ValueError(f"No Strategy class found in {module_path}")
28
+ by_addr = {w["address"].lower(): w for w in CONFIG.get("wallets", [])}
29
+ for key in ("main_wallet", "strategy_wallet"):
30
+ if wallet := config.get(key):
31
+ if entry := by_addr.get(wallet.get("address", "").lower()):
32
+ if pk := entry.get("private_key") or entry.get("private_key_hex"):
33
+ wallet["private_key_hex"] = pk
34
+ return config
56
35
 
57
- main_wallet = config.strategy_config.get("main_wallet") or {}
58
- strategy_wallet = config.strategy_config.get("strategy_wallet") or {}
59
- main_wallet_address = main_wallet.get("address")
60
- strategy_wallet_address = strategy_wallet.get("address")
61
36
 
62
- async def main_wallet_signing_callback(transaction):
63
- private_key = resolve_private_key_for_from_address(
64
- main_wallet_address, config.strategy_config
65
- )
37
+ def create_signing_callback(address: str, config: dict[str, Any]):
38
+ async def sign(transaction: dict) -> str:
39
+ pk = resolve_private_key_for_from_address(address, config)
66
40
  async with web3_from_chain_id(get_transaction_chain_id(transaction)) as web3:
67
- signed = web3.eth.account.sign_transaction(transaction, private_key)
68
- return signed.raw_transaction.hex()
69
-
70
- async def strategy_wallet_signing_callback(transaction):
71
- private_key = resolve_private_key_for_from_address(
72
- strategy_wallet_address,
73
- config.strategy_config,
74
- )
75
- async with web3_from_chain_id(get_transaction_chain_id(transaction)) as web3:
76
- signed = web3.eth.account.sign_transaction(transaction, private_key)
77
- return signed.raw_transaction.hex()
78
-
79
- return strategy_class(
80
- config=config.strategy_config,
81
- main_wallet_signing_callback=main_wallet_signing_callback,
82
- strategy_wallet_signing_callback=strategy_wallet_signing_callback,
83
- )
84
-
85
-
86
- def load_config(
87
- config_path: str | None = None, strategy_name: str | None = None
88
- ) -> StrategyJobConfig:
89
- # Default to config.json if not provided
90
- if not config_path:
91
- config_path = "config.json"
41
+ return web3.eth.account.sign_transaction(
42
+ transaction, pk
43
+ ).raw_transaction.hex()
92
44
 
93
- config_file = Path(config_path)
94
- if not config_file.exists():
95
- raise FileNotFoundError(
96
- f"Config file not found: {config_path}. "
97
- "Please create config.json (see wayfinder_paths/config.example.json for template)"
98
- )
99
-
100
- logger.info(f"Loading config from {config_path}")
101
- with open(config_file) as f:
102
- config_data = json.load(f)
45
+ return sign
103
46
 
104
- config = StrategyJobConfig.from_dict(config_data, strategy_name=strategy_name)
105
- if strategy_name:
106
- config.strategy_config["_strategy_name"] = strategy_name
107
- config.__post_init__()
108
- return config
109
47
 
48
+ def find_strategy_class(module) -> type[Strategy]:
49
+ for _, obj in inspect.getmembers(module, inspect.isclass):
50
+ if issubclass(obj, Strategy) and obj is not Strategy:
51
+ return obj
52
+ raise ValueError(f"No Strategy subclass found in {module.__name__}")
110
53
 
111
- async def run_strategy(
112
- strategy_name: str | None = None,
113
- config_path: str | None = None,
114
- action: str = "run",
115
- **kwargs,
116
- ):
117
- try:
118
- if not strategy_name:
119
- raise ValueError("strategy_name is required")
120
54
 
121
- logger.debug(f"Loading strategy by name: {strategy_name}")
55
+ async def run_strategy(strategy_name: str, action: str = "status", **kw):
56
+ config = get_strategy_config(strategy_name)
122
57
 
123
- logger.debug(f"Config path provided: {config_path}")
124
- config = load_config(config_path, strategy_name=strategy_name)
125
- main_wallet_cfg = config.strategy_config.get("main_wallet") or {}
126
- strategy_wallet_cfg = config.strategy_config.get("strategy_wallet") or {}
127
- logger.debug(
128
- "Loaded config: wallets(main={} strategy={})",
129
- main_wallet_cfg.get("address") or "none",
130
- strategy_wallet_cfg.get("address") or "none",
131
- )
58
+ def signing_cb(key: str):
59
+ if addr := config.get(key, {}).get("address"):
60
+ return create_signing_callback(addr, config)
61
+ return None
132
62
 
133
- # Authentication is via system.api_key in config.json
134
-
135
- strategy = load_strategy(
136
- strategy_name,
137
- config=config,
63
+ module = importlib.import_module(
64
+ f"wayfinder_paths.strategies.{strategy_name}.strategy"
65
+ )
66
+ strategy_cls = find_strategy_class(module)
67
+ strategy = strategy_cls(
68
+ config,
69
+ main_wallet_signing_callback=signing_cb("main_wallet"),
70
+ strategy_wallet_signing_callback=signing_cb("strategy_wallet"),
71
+ )
72
+ await strategy.setup()
73
+
74
+ if action == "policy":
75
+ policies = strategy.policies() if hasattr(strategy, "policies") else []
76
+ if wallet_id := kw.get("wallet_id"):
77
+ policies = [p.replace("FORMAT_WALLET_ID", wallet_id) for p in policies]
78
+ result = {"policies": policies}
79
+ elif action == "status":
80
+ result = await strategy.status()
81
+ elif action == "deposit":
82
+ result = await strategy.deposit(
83
+ main_token_amount=kw.get("main_token_amount", 0.0),
84
+ gas_token_amount=kw.get("gas_token_amount", 0.0),
138
85
  )
139
- logger.info(f"Loaded strategy: {strategy.name}")
140
-
141
- strategy_job = StrategyJob(strategy, config)
142
-
143
- logger.info("Setting up strategy job...")
144
- logger.debug("Auth mode: API key (from system.api_key)")
145
- await strategy_job.setup()
146
-
147
- # Execute action
148
- if action == "run":
149
- logger.info("Starting continuous execution...")
150
- await strategy_job.run_continuous(interval_seconds=kwargs.get("interval"))
151
-
152
- elif action == "deposit":
153
- main_token_amount = kwargs.get("main_token_amount")
154
- gas_token_amount = kwargs.get("gas_token_amount")
155
-
156
- if main_token_amount is None and gas_token_amount is None:
157
- raise ValueError(
158
- "Either main token amount or gas token amount required for deposit (use --main-token-amount and/or --gas-token-amount)"
159
- )
160
-
161
- # Default to 0.0 if not provided
162
- if main_token_amount is None:
163
- main_token_amount = 0.0
164
- if gas_token_amount is None:
165
- gas_token_amount = 0.0
166
-
167
- result = await strategy_job.execute_strategy(
168
- "deposit",
169
- main_token_amount=main_token_amount,
170
- gas_token_amount=gas_token_amount,
171
- )
172
- logger.info(f"Deposit result: {result}")
173
-
174
- elif action == "withdraw":
175
- amount = kwargs.get("amount")
176
- result = await strategy_job.execute_strategy("withdraw", amount=amount)
177
- logger.info(f"Withdraw result: {result}")
178
-
179
- elif action == "status":
180
- result = await strategy_job.execute_strategy("status")
181
- logger.info(f"Status: {json.dumps(result, indent=2)}")
182
-
183
- elif action == "update":
184
- result = await strategy_job.execute_strategy("update")
185
- logger.info(f"Update result: {result}")
186
-
187
- elif action == "exit":
188
- result = await strategy_job.execute_strategy("exit")
189
- logger.info(f"Exit result: {result}")
190
-
191
- elif action == "partial-liquidate":
192
- usd_value = kwargs.get("amount")
193
- if not usd_value:
194
- raise ValueError("Amount (USD value) required for partial-liquidate")
195
- result = await strategy_job.execute_strategy(
196
- "partial_liquidate", usd_value=usd_value
197
- )
198
- logger.info(f"Partial liquidation result: {result}")
199
-
200
- elif action == "policy":
201
- policies: list[str] = []
202
-
86
+ elif action == "withdraw":
87
+ result = await strategy.withdraw(amount=kw.get("amount"))
88
+ elif action == "update":
89
+ result = await strategy.update()
90
+ elif action == "exit":
91
+ result = await strategy.exit()
92
+ elif action == "run":
93
+ while True:
203
94
  try:
204
- spols = getattr(strategy, "policies", None)
205
- if callable(spols):
206
- result = spols() # type: ignore[misc]
207
- if isinstance(result, list) and result:
208
- policies = [p for p in result if isinstance(p, str)]
209
- except Exception:
210
- pass
211
-
212
- seen = set()
213
- deduped: list[str] = []
214
- for p in policies:
215
- if p not in seen:
216
- seen.add(p)
217
- deduped.append(p)
218
-
219
- wallet_id = kwargs.get("wallet_id")
220
- if not wallet_id:
221
- wallet_id = config.strategy_config.get("wallet_id")
222
- if not wallet_id:
223
- wallet_id = config.system.wallet_id
224
-
225
- # Render policies with wallet_id if available
226
- if wallet_id:
227
- rendered = [
228
- p.replace("FORMAT_WALLET_ID", str(wallet_id)) for p in deduped
229
- ]
230
- else:
231
- rendered = deduped
232
- logger.info(
233
- "Policy rendering without wallet_id - policies contain FORMAT_WALLET_ID placeholder"
234
- )
235
-
236
- logger.info(json.dumps({"policies": rendered}, indent=2))
237
-
238
- elif action == "script":
239
- duration = kwargs.get("duration") or 300
240
- logger.info(f"Running script mode for {duration}s...")
241
- task = asyncio.create_task(
242
- strategy_job.run_continuous(
243
- interval_seconds=kwargs.get("interval") or 60
244
- )
245
- )
246
- await asyncio.sleep(duration)
247
- task.cancel()
248
- try:
249
- await task
95
+ result = await strategy.update()
96
+ logger.info(f"Update: {result}")
97
+ await asyncio.sleep(kw.get("interval", 60))
250
98
  except asyncio.CancelledError:
251
- logger.info("Script mode execution completed")
252
-
253
- else:
254
- raise ValueError(f"Unknown action: {action}")
255
-
256
- except KeyboardInterrupt:
257
- logger.info("Shutting down...")
258
- except Exception as e:
259
- logger.error(f"Error: {e}")
260
- sys.exit(1)
261
- finally:
262
- if "strategy_job" in locals():
263
- await strategy_job.stop()
99
+ result = (True, "stopped")
100
+ break
101
+ else:
102
+ raise ValueError(f"Unknown action: {action}")
103
+
104
+ print(
105
+ json.dumps(result, indent=2)
106
+ if isinstance(result, dict)
107
+ else f"{action}: {result}"
108
+ )
264
109
 
265
110
 
266
111
  def main():
267
- parser = argparse.ArgumentParser(description="Run strategy strategies")
268
- parser.add_argument(
269
- "strategy",
270
- help="Strategy to run (stablecoin_yield_strategy)",
271
- )
272
- parser.add_argument(
273
- "--config", help="Path to config file (defaults to config.json)"
274
- )
275
- parser.add_argument(
112
+ p = argparse.ArgumentParser()
113
+ p.add_argument("strategy")
114
+ p.add_argument(
276
115
  "--action",
277
- default="run",
278
- choices=[
279
- "run",
280
- "deposit",
281
- "withdraw",
282
- "status",
283
- "update",
284
- "exit",
285
- "policy",
286
- "script",
287
- "partial-liquidate",
288
- ],
289
- help="Action to perform (default: run)",
116
+ default="status",
117
+ choices=["run", "deposit", "withdraw", "status", "update", "exit", "policy"],
290
118
  )
291
- parser.add_argument(
292
- "--amount",
293
- type=float,
294
- help="Amount for withdraw/partial-liquidate actions",
119
+ p.add_argument("--amount", type=float)
120
+ p.add_argument("--main-token-amount", type=float, dest="main_token_amount")
121
+ p.add_argument(
122
+ "--gas-token-amount", type=float, dest="gas_token_amount", default=0.0
295
123
  )
296
- parser.add_argument(
297
- "--main-token-amount",
298
- "--main_token_amount",
299
- type=float,
300
- dest="main_token_amount",
301
- help="Main token amount for deposit action",
302
- )
303
- parser.add_argument(
304
- "--gas-token-amount",
305
- "--gas_token_amount",
306
- type=float,
307
- dest="gas_token_amount",
308
- default=0.0,
309
- help="Gas token amount for deposit action (default: 0.0)",
310
- )
311
- parser.add_argument(
312
- "--interval",
313
- type=int,
314
- help="Update interval in seconds for continuous/script modes",
315
- )
316
- parser.add_argument(
317
- "--duration", type=int, help="Duration in seconds for script action"
318
- )
319
- parser.add_argument("--debug", action="store_true", help="Enable debug logging")
320
- parser.add_argument(
321
- "--wallet-id",
322
- help="Wallet ID for policy rendering (replaces FORMAT_WALLET_ID in policies)",
323
- )
324
-
325
- args = parser.parse_args()
124
+ p.add_argument("--interval", type=int, default=60)
125
+ p.add_argument("--wallet-id", dest="wallet_id")
126
+ p.add_argument("--debug", action="store_true")
127
+ args = p.parse_args()
326
128
 
327
- # Configure logging
328
- log_level = "DEBUG" if args.debug else "INFO"
329
129
  logger.remove()
330
- logger.add(sys.stderr, level=log_level)
130
+ logger.add(sys.stderr, level="DEBUG" if args.debug else "INFO")
331
131
 
332
- # Run strategy
333
132
  asyncio.run(
334
133
  run_strategy(
335
- strategy_name=args.strategy,
336
- config_path=args.config,
337
- action=args.action,
134
+ args.strategy,
135
+ args.action,
338
136
  amount=args.amount,
339
137
  main_token_amount=args.main_token_amount,
340
138
  gas_token_amount=args.gas_token_amount,
341
139
  interval=args.interval,
342
- duration=args.duration,
343
- wallet_id=getattr(args, "wallet_id", None),
140
+ wallet_id=args.wallet_id,
344
141
  )
345
142
  )
346
143
 
@@ -1 +1,3 @@
1
- USDC_ARBITRUM_TOKEN_ID = "usd-coin-arbitrum"
1
+ from wayfinder_paths.core.constants.tokens import TOKEN_ID_USDC_ARBITRUM
2
+
3
+ USDC_ARBITRUM_TOKEN_ID = TOKEN_ID_USDC_ARBITRUM