wayfinder-paths 0.1.9__py3-none-any.whl → 0.1.11__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 (54) hide show
  1. wayfinder_paths/adapters/balance_adapter/README.md +1 -2
  2. wayfinder_paths/adapters/balance_adapter/adapter.py +4 -4
  3. wayfinder_paths/adapters/brap_adapter/adapter.py +139 -23
  4. wayfinder_paths/adapters/moonwell_adapter/README.md +174 -0
  5. wayfinder_paths/adapters/moonwell_adapter/__init__.py +7 -0
  6. wayfinder_paths/adapters/moonwell_adapter/adapter.py +1226 -0
  7. wayfinder_paths/adapters/moonwell_adapter/test_adapter.py +635 -0
  8. wayfinder_paths/core/clients/AuthClient.py +3 -0
  9. wayfinder_paths/core/clients/WayfinderClient.py +2 -2
  10. wayfinder_paths/core/constants/__init__.py +0 -2
  11. wayfinder_paths/core/constants/base.py +6 -2
  12. wayfinder_paths/core/constants/moonwell_abi.py +411 -0
  13. wayfinder_paths/core/engine/StrategyJob.py +3 -0
  14. wayfinder_paths/core/services/local_evm_txn.py +182 -217
  15. wayfinder_paths/core/services/local_token_txn.py +46 -26
  16. wayfinder_paths/core/strategies/descriptors.py +1 -1
  17. wayfinder_paths/core/utils/evm_helpers.py +0 -27
  18. wayfinder_paths/run_strategy.py +34 -74
  19. wayfinder_paths/scripts/create_strategy.py +2 -27
  20. wayfinder_paths/scripts/run_strategy.py +37 -7
  21. wayfinder_paths/strategies/basis_trading_strategy/strategy.py +1 -1
  22. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +0 -15
  23. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +1 -1
  24. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/README.md +108 -0
  25. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/examples.json +11 -0
  26. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/strategy.py +2975 -0
  27. wayfinder_paths/strategies/moonwell_wsteth_loop_strategy/test_strategy.py +886 -0
  28. wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +0 -7
  29. wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +1 -1
  30. wayfinder_paths/templates/adapter/README.md +5 -21
  31. wayfinder_paths/templates/adapter/adapter.py +1 -2
  32. wayfinder_paths/templates/adapter/test_adapter.py +1 -1
  33. wayfinder_paths/templates/strategy/README.md +4 -21
  34. wayfinder_paths/tests/test_smoke_manifest.py +17 -2
  35. {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.11.dist-info}/METADATA +60 -187
  36. {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.11.dist-info}/RECORD +38 -45
  37. wayfinder_paths/CONFIG_GUIDE.md +0 -390
  38. wayfinder_paths/adapters/balance_adapter/manifest.yaml +0 -8
  39. wayfinder_paths/adapters/brap_adapter/manifest.yaml +0 -11
  40. wayfinder_paths/adapters/hyperlend_adapter/manifest.yaml +0 -10
  41. wayfinder_paths/adapters/hyperliquid_adapter/manifest.yaml +0 -8
  42. wayfinder_paths/adapters/ledger_adapter/manifest.yaml +0 -11
  43. wayfinder_paths/adapters/pool_adapter/manifest.yaml +0 -10
  44. wayfinder_paths/adapters/token_adapter/manifest.yaml +0 -6
  45. wayfinder_paths/config.example.json +0 -22
  46. wayfinder_paths/core/engine/manifest.py +0 -97
  47. wayfinder_paths/scripts/validate_manifests.py +0 -213
  48. wayfinder_paths/strategies/basis_trading_strategy/manifest.yaml +0 -23
  49. wayfinder_paths/strategies/hyperlend_stable_yield_strategy/manifest.yaml +0 -7
  50. wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +0 -17
  51. wayfinder_paths/templates/adapter/manifest.yaml +0 -6
  52. wayfinder_paths/templates/strategy/manifest.yaml +0 -8
  53. {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.11.dist-info}/LICENSE +0 -0
  54. {wayfinder_paths-0.1.9.dist-info → wayfinder_paths-0.1.11.dist-info}/WHEEL +0 -0
@@ -1,22 +0,0 @@
1
- {
2
- "user": {
3
- "username": "your_username",
4
- "password": "your_password",
5
- "refresh_token": null,
6
- "api_key": "sk_live_abc123..."
7
- },
8
- "system": {
9
- "api_base_url": "https://wayfinder.ai/api/v1",
10
- "api_key": "sk_live_abc123...",
11
- "wallets_path": "wallets.json"
12
- },
13
- "strategy": {
14
- "rpc_urls": {
15
- "1": "https://eth.llamarpc.com",
16
- "42161": "https://arb1.arbitrum.io/rpc",
17
- "8453": "https://mainnet.base.org",
18
- "solana": "https://api.mainnet-beta.solana.com",
19
- "999": "https://rpc.hyperliquid.xyz/evm"
20
- }
21
- }
22
- }
@@ -1,97 +0,0 @@
1
- from typing import Any
2
-
3
- import yaml
4
- from pydantic import BaseModel, Field, validator
5
-
6
-
7
- class AdapterRequirement(BaseModel):
8
- name: str = Field(
9
- ..., description="Adapter symbolic name (e.g., BALANCE, HYPERLIQUID)"
10
- )
11
- capabilities: list[str] = Field(default_factory=list)
12
-
13
-
14
- class AdapterManifest(BaseModel):
15
- schema_version: str = Field(default="0.1")
16
- entrypoint: str
17
- capabilities: list[str] = Field(default_factory=list)
18
- dependencies: list[str] = Field(default_factory=list)
19
-
20
- @validator("entrypoint")
21
- def validate_entrypoint(cls, v: str) -> str:
22
- if "." not in v:
23
- raise ValueError("entrypoint must be a full import path")
24
- return v
25
-
26
- @validator("capabilities")
27
- def validate_capabilities(cls, v: list[str]) -> list[str]:
28
- if not v:
29
- raise ValueError("capabilities cannot be empty")
30
- return v
31
-
32
- @validator("dependencies")
33
- def validate_dependencies(cls, v: list[str]) -> list[str]:
34
- if not v:
35
- raise ValueError("dependencies cannot be empty")
36
- return v
37
-
38
-
39
- class StrategyManifest(BaseModel):
40
- schema_version: str = Field(default="0.1")
41
- entrypoint: str = Field(
42
- ...,
43
- description="Python path to class, e.g. strategies.funding_rate_strategy.FundingRateStrategy",
44
- )
45
- name: str | None = Field(
46
- default=None,
47
- description="Unique name identifier for this strategy instance. Used to look up dedicated wallet in wallets.json by label.",
48
- )
49
- permissions: dict[str, Any] = Field(default_factory=dict)
50
- adapters: list[AdapterRequirement] = Field(default_factory=list)
51
-
52
- @validator("entrypoint")
53
- def validate_entrypoint(cls, v: str) -> str:
54
- if "." not in v:
55
- raise ValueError(
56
- "entrypoint must be a full import path to a Strategy class"
57
- )
58
- return v
59
-
60
- @validator("permissions")
61
- def validate_permissions(cls, v: dict) -> dict:
62
- if "policy" not in v:
63
- raise ValueError("permissions.policy is required")
64
- if not v["policy"]:
65
- raise ValueError("permissions.policy cannot be empty")
66
- return v
67
-
68
- @validator("adapters")
69
- def validate_adapters(cls, v: list) -> list:
70
- if not v:
71
- raise ValueError("adapters cannot be empty")
72
- return v
73
-
74
-
75
- def load_adapter_manifest(path: str) -> AdapterManifest:
76
- with open(path) as f:
77
- data = yaml.safe_load(f)
78
- return AdapterManifest(**data)
79
-
80
-
81
- def load_strategy_manifest(path: str) -> StrategyManifest:
82
- with open(path) as f:
83
- data = yaml.safe_load(f)
84
- return StrategyManifest(**data)
85
-
86
-
87
- def load_manifest(path: str) -> StrategyManifest:
88
- """Legacy function for backward compatibility."""
89
- return load_strategy_manifest(path)
90
-
91
-
92
- def validate_manifest(manifest: StrategyManifest) -> None:
93
- # Simple v0.1 rules: require at least one adapter and permissions.policy
94
- if not manifest.adapters:
95
- raise ValueError("Manifest must declare at least one adapter")
96
- if "policy" not in manifest.permissions:
97
- raise ValueError("Manifest.permissions must include 'policy'")
@@ -1,213 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Manifest Validator
4
-
5
- Validates all adapter and strategy manifests in the repository.
6
- """
7
-
8
- import sys
9
- from pathlib import Path
10
-
11
- from loguru import logger
12
-
13
- # Add parent to path for imports
14
- sys.path.insert(0, str(Path(__file__).parent.parent))
15
-
16
- from wayfinder_paths.core.adapters.BaseAdapter import BaseAdapter
17
- from wayfinder_paths.core.engine.manifest import (
18
- load_adapter_manifest,
19
- load_strategy_manifest,
20
- )
21
- from wayfinder_paths.core.strategies.Strategy import Strategy
22
-
23
-
24
- def verify_entrypoint(entrypoint: str) -> tuple[bool, str | None]:
25
- """Verify entrypoint is importable. Returns (success, error_message)."""
26
- try:
27
- module_path, class_name = entrypoint.rsplit(".", 1)
28
- module = __import__(module_path, fromlist=[class_name], level=0)
29
- getattr(module, class_name) # Verify class exists
30
- return True, None
31
- except ImportError as e:
32
- return False, f"Import error: {str(e)}"
33
- except AttributeError as e:
34
- return False, f"Class not found: {str(e)}"
35
- except Exception as e:
36
- return False, f"Unexpected error: {str(e)}"
37
-
38
-
39
- def verify_adapter_class(entrypoint: str) -> tuple[bool, str | None]:
40
- """Verify entrypoint is an adapter class."""
41
- valid, error = verify_entrypoint(entrypoint)
42
- if not valid:
43
- return False, error
44
-
45
- try:
46
- module_path, class_name = entrypoint.rsplit(".", 1)
47
- module = __import__(module_path, fromlist=[class_name], level=0)
48
- adapter_class = getattr(module, class_name)
49
-
50
- if not issubclass(adapter_class, BaseAdapter):
51
- return False, f"{class_name} is not a BaseAdapter"
52
- return True, None
53
- except Exception as e:
54
- return False, f"Error verifying adapter: {str(e)}"
55
-
56
-
57
- def verify_strategy_class(entrypoint: str) -> tuple[bool, str | None]:
58
- """Verify entrypoint is a strategy class."""
59
- valid, error = verify_entrypoint(entrypoint)
60
- if not valid:
61
- return False, error
62
-
63
- try:
64
- module_path, class_name = entrypoint.rsplit(".", 1)
65
- module = __import__(module_path, fromlist=[class_name], level=0)
66
- strategy_class = getattr(module, class_name)
67
-
68
- if not issubclass(strategy_class, Strategy):
69
- return False, f"{class_name} is not a Strategy"
70
- return True, None
71
- except Exception as e:
72
- return False, f"Error verifying strategy: {str(e)}"
73
-
74
-
75
- # Capabilities are only declared in manifest - no code validation needed
76
- # Manifest is the single source of truth for capabilities
77
-
78
-
79
- def verify_dependencies(dependencies: list[str]) -> tuple[bool, list[str]]:
80
- """Verify dependencies are importable. Returns (valid, error_messages)."""
81
- errors = []
82
-
83
- for dep in dependencies:
84
- # Try to import from core.clients
85
- try:
86
- __import__(f"core.clients.{dep}", fromlist=[dep], level=0)
87
- except ImportError:
88
- errors.append(f"Dependency '{dep}' not found in core.clients")
89
- except Exception as e:
90
- errors.append(f"Error importing dependency '{dep}': {str(e)}")
91
-
92
- return len(errors) == 0, errors
93
-
94
-
95
- def validate_adapter_manifest(manifest_path: str) -> tuple[bool, list[str]]:
96
- """Validate adapter manifest. Returns (valid, error_messages)."""
97
- errors = []
98
-
99
- try:
100
- manifest = load_adapter_manifest(manifest_path)
101
- except Exception as e:
102
- return False, [f"Schema error: {str(e)}"]
103
-
104
- # Verify entrypoint
105
- valid, error = verify_adapter_class(manifest.entrypoint)
106
- if not valid:
107
- errors.append(f"Entrypoint validation failed: {error}")
108
- return False, errors
109
-
110
- # Verify dependencies
111
- valid, dep_errors = verify_dependencies(manifest.dependencies)
112
- if not valid:
113
- errors.extend(dep_errors)
114
-
115
- return len(errors) == 0, errors
116
-
117
-
118
- def validate_strategy_manifest(manifest_path: str) -> tuple[bool, list[str]]:
119
- """Validate strategy manifest. Returns (valid, error_messages)."""
120
- errors = []
121
-
122
- try:
123
- manifest = load_strategy_manifest(manifest_path)
124
- except Exception as e:
125
- return False, [f"Schema error: {str(e)}"]
126
-
127
- # Verify entrypoint
128
- valid, error = verify_strategy_class(manifest.entrypoint)
129
- if not valid:
130
- errors.append(f"Entrypoint validation failed: {error}")
131
- return False, errors
132
-
133
- # Permissions are already validated by Pydantic model
134
-
135
- return len(errors) == 0, errors
136
-
137
-
138
- def find_adapter_manifests() -> list[Path]:
139
- """Find all adapter manifest files."""
140
- manifests = []
141
- adapter_dir = Path(__file__).parent.parent / "adapters"
142
- if adapter_dir.exists():
143
- for adapter_path in adapter_dir.iterdir():
144
- manifest_path = adapter_path / "manifest.yaml"
145
- if manifest_path.exists():
146
- manifests.append(manifest_path)
147
- return manifests
148
-
149
-
150
- def find_strategy_manifests() -> list[Path]:
151
- """Find all strategy manifest files."""
152
- manifests = []
153
- strategy_dir = Path(__file__).parent.parent / "strategies"
154
- if strategy_dir.exists():
155
- for strategy_path in strategy_dir.iterdir():
156
- manifest_path = strategy_path / "manifest.yaml"
157
- if manifest_path.exists():
158
- manifests.append(manifest_path)
159
- return manifests
160
-
161
-
162
- def main() -> int:
163
- """Main validation function. Returns 0 on success, 1 on failure."""
164
- logger.info("Validating all manifests...")
165
-
166
- all_valid = True
167
- error_count = 0
168
-
169
- # Validate adapter manifests
170
- logger.info("\n=== Validating Adapter Manifests ===")
171
- adapter_manifests = find_adapter_manifests()
172
- for manifest_path in sorted(adapter_manifests):
173
- logger.info(f"Validating {manifest_path}...")
174
- valid, errors = validate_adapter_manifest(str(manifest_path))
175
- if valid:
176
- logger.success(f"✅ {manifest_path.name} - Valid")
177
- else:
178
- logger.error(f"❌ {manifest_path.name} - Invalid")
179
- for error in errors:
180
- logger.error(f" {error}")
181
- all_valid = False
182
- error_count += len(errors)
183
-
184
- # Validate strategy manifests
185
- logger.info("\n=== Validating Strategy Manifests ===")
186
- strategy_manifests = find_strategy_manifests()
187
- for manifest_path in sorted(strategy_manifests):
188
- logger.info(f"Validating {manifest_path}...")
189
- valid, errors = validate_strategy_manifest(str(manifest_path))
190
- if valid:
191
- logger.success(f"✅ {manifest_path.name} - Valid")
192
- else:
193
- logger.error(f"❌ {manifest_path.name} - Invalid")
194
- for error in errors:
195
- logger.error(f" {error}")
196
- all_valid = False
197
- error_count += len(errors)
198
-
199
- # Summary
200
- logger.info("\n=== Summary ===")
201
- if all_valid:
202
- logger.success(
203
- f"✅ All manifests valid! ({len(adapter_manifests)} adapters, "
204
- f"{len(strategy_manifests)} strategies)"
205
- )
206
- return 0
207
- else:
208
- logger.error(f"❌ Validation failed with {error_count} error(s)")
209
- return 1
210
-
211
-
212
- if __name__ == "__main__":
213
- sys.exit(main())
@@ -1,23 +0,0 @@
1
- schema_version: "0.1"
2
- entrypoint: "wayfinder_paths.strategies.basis_trading_strategy.strategy.BasisTradingStrategy"
3
- permissions:
4
- policy: |
5
- (wallet.id == 'FORMAT_WALLET_ID') AND (
6
- # Allow Hyperliquid EIP-712 order actions
7
- (action.type == 'hyperliquid_order') OR
8
- (action.type == 'hyperliquid_cancel') OR
9
- (action.type == 'hyperliquid_transfer') OR
10
- # Allow USDC transfers to Hyperliquid bridge
11
- (action.type == 'erc20_transfer' AND action.to == '0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7') OR
12
- # Allow USDC withdraw to main wallet
13
- (action.type == 'erc20_transfer' AND action.to == main_wallet.address)
14
- )
15
- adapters:
16
- - name: "BALANCE"
17
- capabilities: ["wallet_read", "wallet_transfer"]
18
- - name: "LEDGER"
19
- capabilities: ["ledger.read", "ledger.write", "strategy.transactions"]
20
- - name: "TOKEN"
21
- capabilities: ["token.read"]
22
- - name: "HYPERLIQUID"
23
- capabilities: ["market.read", "market.meta", "market.funding", "market.candles", "market.orderbook", "order.execute", "order.cancel", "position.manage", "transfer"]
@@ -1,7 +0,0 @@
1
- schema_version: "0.1"
2
- entrypoint: "strategies.hyperlend_stable_yield_strategy.strategy.HyperlendStableYieldStrategy"
3
- permissions:
4
- policy: "(wallet.id == 'FORMAT_WALLET_ID')"
5
- adapters:
6
- - name: "BALANCE"
7
- capabilities: ["wallet_read"]
@@ -1,17 +0,0 @@
1
- schema_version: "0.1"
2
- entrypoint: "strategies.stablecoin_yield_strategy.strategy.StablecoinYieldStrategy"
3
- permissions:
4
- policy: "(wallet.id == 'FORMAT_WALLET_ID') && ((eth.tx.data[0..10] == '0x095ea7b3' && eth.tx.data[34..74] == 'f75584ef6673ad213a685a1b58cc0330b8ea22cf') || (eth.tx.to == '0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf'))"
5
- adapters:
6
- - name: "BALANCE"
7
- capabilities: ["wallet_read", "wallet_transfer"]
8
- - name: "POOL"
9
- capabilities: ["pool.read", "pool.analytics"]
10
- - name: "BRAP"
11
- capabilities: ["swap.quote", "swap.execute"]
12
- - name: "TOKEN"
13
- capabilities: ["token.read"]
14
- - name: "LEDGER"
15
- capabilities: ["ledger.read", "strategy.transactions"]
16
- - name: "EVM_TRANSACTION"
17
- capabilities: ["wallet_transfer"]
@@ -1,6 +0,0 @@
1
- schema_version: "0.1"
2
- entrypoint: "adapters.my_adapter.adapter.MyAdapter"
3
- capabilities:
4
- - "example.op"
5
- dependencies:
6
- - "MyClient"
@@ -1,8 +0,0 @@
1
- schema_version: "0.1"
2
- entrypoint: "strategies.my_strategy.strategy.MyStrategy"
3
- name: "my_strategy" # Unique name for this strategy instance (used for wallet lookup)
4
- permissions:
5
- policy: "(wallet.id == 'FORMAT_WALLET_ID')"
6
- adapters:
7
- - name: "BALANCE"
8
- capabilities: ["wallet_read"]