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
|
@@ -17,7 +17,7 @@ The adapter uses the PoolClient which automatically handles authentication and A
|
|
|
17
17
|
The PoolClient will automatically:
|
|
18
18
|
|
|
19
19
|
- Use the WAYFINDER_API_URL from settings
|
|
20
|
-
- Handle authentication via
|
|
20
|
+
- Handle authentication via config.json
|
|
21
21
|
- Manage token refresh and retry logic
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
@@ -45,41 +45,6 @@ else:
|
|
|
45
45
|
print(f"Error: {data}")
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
### Find High Yield Pools
|
|
49
|
-
|
|
50
|
-
```python
|
|
51
|
-
success, data = await adapter.find_high_yield_pools(
|
|
52
|
-
min_apy=0.03, # 3% minimum APY
|
|
53
|
-
min_tvl=500000, # $500k minimum TVL
|
|
54
|
-
stablecoin_only=True,
|
|
55
|
-
network_codes=["base", "ethereum"]
|
|
56
|
-
)
|
|
57
|
-
if success:
|
|
58
|
-
pools = data.get("pools", [])
|
|
59
|
-
print(f"Found {len(pools)} high-yield pools")
|
|
60
|
-
for pool in pools:
|
|
61
|
-
print(f"Pool: {pool.get('id')} - APY: {pool.get('llama_apy_pct')}%")
|
|
62
|
-
else:
|
|
63
|
-
print(f"Error: {data}")
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Get Pool Analytics
|
|
67
|
-
|
|
68
|
-
```python
|
|
69
|
-
success, data = await adapter.get_pool_analytics(
|
|
70
|
-
pool_ids=["pool-123", "pool-456"]
|
|
71
|
-
)
|
|
72
|
-
if success:
|
|
73
|
-
analytics = data.get("analytics", [])
|
|
74
|
-
for pool_analytics in analytics:
|
|
75
|
-
pool = pool_analytics.get("pool", {})
|
|
76
|
-
combined_apy = pool_analytics.get("combined_apy", 0)
|
|
77
|
-
tvl_usd = pool_analytics.get("tvl_usd", 0)
|
|
78
|
-
print(f"Pool: {pool.get('name')} - APY: {combined_apy:.2%} - TVL: ${tvl_usd:,.0f}")
|
|
79
|
-
else:
|
|
80
|
-
print(f"Error: {data}")
|
|
81
|
-
```
|
|
82
|
-
|
|
83
48
|
### Get Llama Matches
|
|
84
49
|
|
|
85
50
|
```python
|
|
@@ -110,47 +75,6 @@ else:
|
|
|
110
75
|
|
|
111
76
|
## Advanced Usage
|
|
112
77
|
|
|
113
|
-
### Filtering High Yield Pools
|
|
114
|
-
|
|
115
|
-
The `find_high_yield_pools` method provides powerful filtering capabilities:
|
|
116
|
-
|
|
117
|
-
```python
|
|
118
|
-
# Find stablecoin pools with high yield on specific networks
|
|
119
|
-
success, data = await adapter.find_high_yield_pools(
|
|
120
|
-
min_apy=0.05, # 5% minimum APY
|
|
121
|
-
min_tvl=1000000, # $1M minimum TVL
|
|
122
|
-
stablecoin_only=True, # Only stablecoin pools
|
|
123
|
-
network_codes=["base", "arbitrum"] # Specific networks
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
if success:
|
|
127
|
-
pools = data.get("pools", [])
|
|
128
|
-
# Pools are automatically sorted by APY (highest first)
|
|
129
|
-
best_pool = pools[0] if pools else None
|
|
130
|
-
if best_pool:
|
|
131
|
-
print(f"Best pool: {best_pool.get('id')} - APY: {best_pool.get('llama_apy_pct')}%")
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Comprehensive Pool Analysis
|
|
135
|
-
|
|
136
|
-
```python
|
|
137
|
-
# Get detailed analytics for specific pools
|
|
138
|
-
success, data = await adapter.get_pool_analytics(["pool-123"])
|
|
139
|
-
|
|
140
|
-
if success:
|
|
141
|
-
analytics = data.get("analytics", [])
|
|
142
|
-
for pool_analytics in analytics:
|
|
143
|
-
pool = pool_analytics.get("pool", {})
|
|
144
|
-
llama_data = pool_analytics.get("llama_data", {})
|
|
145
|
-
|
|
146
|
-
print(f"Pool: {pool.get('name')}")
|
|
147
|
-
print(f" Combined APY: {pool_analytics.get('combined_apy', 0):.2%}")
|
|
148
|
-
print(f" TVL: ${pool_analytics.get('tvl_usd', 0):,.0f}")
|
|
149
|
-
print(f" Llama APY: {llama_data.get('llama_apy_pct', 0)}%")
|
|
150
|
-
print(f" Stablecoin: {llama_data.get('llama_stablecoin', False)}")
|
|
151
|
-
print(f" IL Risk: {llama_data.get('llama_il_risk', 'unknown')}")
|
|
152
|
-
```
|
|
153
|
-
|
|
154
78
|
## API Endpoints
|
|
155
79
|
|
|
156
80
|
The adapter uses the following Wayfinder API endpoints:
|
|
@@ -86,125 +86,3 @@ class PoolAdapter(BaseAdapter):
|
|
|
86
86
|
except Exception as e:
|
|
87
87
|
self.logger.error(f"Error fetching Llama reports: {e}")
|
|
88
88
|
return (False, str(e))
|
|
89
|
-
|
|
90
|
-
async def find_high_yield_pools(
|
|
91
|
-
self,
|
|
92
|
-
min_apy: float = 0.01,
|
|
93
|
-
min_tvl: float = 1000000,
|
|
94
|
-
stablecoin_only: bool = True,
|
|
95
|
-
network_codes: list[str] | None = None,
|
|
96
|
-
) -> tuple[bool, Any]:
|
|
97
|
-
"""
|
|
98
|
-
Find high-yield pools based on criteria.
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
min_apy: Minimum APY threshold (as decimal, e.g., 0.01 for 1%)
|
|
102
|
-
min_tvl: Minimum TVL threshold in USD
|
|
103
|
-
stablecoin_only: Whether to filter for stablecoin pools only
|
|
104
|
-
network_codes: List of network codes to filter by
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
Tuple of (success, data) where data is filtered pools or error message
|
|
108
|
-
"""
|
|
109
|
-
try:
|
|
110
|
-
# Get Llama matches for yield data
|
|
111
|
-
success, llama_data = await self.get_llama_matches()
|
|
112
|
-
if not success:
|
|
113
|
-
return (False, f"Failed to fetch Llama data: {llama_data}")
|
|
114
|
-
|
|
115
|
-
matches = llama_data.get("matches", [])
|
|
116
|
-
filtered_pools = []
|
|
117
|
-
|
|
118
|
-
for pool in matches:
|
|
119
|
-
# Apply filters
|
|
120
|
-
if stablecoin_only and not pool.get("llama_stablecoin", False):
|
|
121
|
-
continue
|
|
122
|
-
|
|
123
|
-
if pool.get("llama_tvl_usd", 0) < min_tvl:
|
|
124
|
-
continue
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
pool.get("llama_apy_pct", 0) < min_apy * 100
|
|
128
|
-
): # Convert to percentage
|
|
129
|
-
continue
|
|
130
|
-
|
|
131
|
-
if network_codes and pool.get("network", "").lower() not in [
|
|
132
|
-
nc.lower() for nc in network_codes
|
|
133
|
-
]:
|
|
134
|
-
continue
|
|
135
|
-
|
|
136
|
-
filtered_pools.append(pool)
|
|
137
|
-
|
|
138
|
-
# Sort by APY descending
|
|
139
|
-
filtered_pools.sort(key=lambda x: x.get("llama_apy_pct", 0), reverse=True)
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
True,
|
|
143
|
-
{
|
|
144
|
-
"pools": filtered_pools,
|
|
145
|
-
"total_found": len(filtered_pools),
|
|
146
|
-
"filters_applied": {
|
|
147
|
-
"min_apy": min_apy,
|
|
148
|
-
"min_tvl": min_tvl,
|
|
149
|
-
"stablecoin_only": stablecoin_only,
|
|
150
|
-
"network_codes": network_codes,
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
)
|
|
154
|
-
except Exception as e:
|
|
155
|
-
self.logger.error(f"Error finding high yield pools: {e}")
|
|
156
|
-
return (False, str(e))
|
|
157
|
-
|
|
158
|
-
async def get_pool_analytics(self, pool_ids: list[str]) -> tuple[bool, Any]:
|
|
159
|
-
"""
|
|
160
|
-
Get comprehensive analytics for specific pools.
|
|
161
|
-
|
|
162
|
-
Args:
|
|
163
|
-
pool_ids: List of pool identifiers
|
|
164
|
-
|
|
165
|
-
Returns:
|
|
166
|
-
Tuple of (success, data) where data is pool analytics or error message
|
|
167
|
-
"""
|
|
168
|
-
try:
|
|
169
|
-
# Get pool data
|
|
170
|
-
success, pool_data = await self.get_pools_by_ids(pool_ids)
|
|
171
|
-
if not success:
|
|
172
|
-
return (False, f"Failed to fetch pool data: {pool_data}")
|
|
173
|
-
|
|
174
|
-
# Get Llama reports
|
|
175
|
-
success, llama_data = await self.get_llama_reports(pool_ids)
|
|
176
|
-
if not success:
|
|
177
|
-
self.logger.warning(f"Failed to fetch Llama data: {llama_data}")
|
|
178
|
-
llama_data = {}
|
|
179
|
-
|
|
180
|
-
pools = pool_data.get("pools", [])
|
|
181
|
-
llama_reports = llama_data
|
|
182
|
-
|
|
183
|
-
# Combine data
|
|
184
|
-
analytics = []
|
|
185
|
-
for pool in pools:
|
|
186
|
-
pool_id = pool.get("id")
|
|
187
|
-
llama_report = llama_reports.get(pool_id.lower()) if pool_id else None
|
|
188
|
-
|
|
189
|
-
analytics.append(
|
|
190
|
-
{
|
|
191
|
-
"pool": pool,
|
|
192
|
-
"llama_data": llama_report,
|
|
193
|
-
"combined_apy": (
|
|
194
|
-
llama_report.get("llama_combined_apy_pct", 0) / 100
|
|
195
|
-
if llama_report
|
|
196
|
-
and llama_report.get("llama_combined_apy_pct") is not None
|
|
197
|
-
else pool.get("apy", 0)
|
|
198
|
-
),
|
|
199
|
-
"tvl_usd": (
|
|
200
|
-
llama_report.get("llama_tvl_usd", 0)
|
|
201
|
-
if llama_report and llama_report.get("llama_tvl_usd")
|
|
202
|
-
else pool.get("tvl", 0)
|
|
203
|
-
),
|
|
204
|
-
}
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
return (True, {"analytics": analytics, "total_pools": len(analytics)})
|
|
208
|
-
except Exception as e:
|
|
209
|
-
self.logger.error(f"Error getting pool analytics: {e}")
|
|
210
|
-
return (False, str(e))
|
|
@@ -39,62 +39,5 @@
|
|
|
39
39
|
]
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
},
|
|
43
|
-
"find_high_yield_pools": {
|
|
44
|
-
"description": "Find high-yield pools based on criteria",
|
|
45
|
-
"input": {
|
|
46
|
-
"min_apy": 0.03,
|
|
47
|
-
"min_tvl": 500000,
|
|
48
|
-
"stablecoin_only": true,
|
|
49
|
-
"network_codes": ["base", "ethereum"]
|
|
50
|
-
},
|
|
51
|
-
"output": {
|
|
52
|
-
"success": true,
|
|
53
|
-
"data": {
|
|
54
|
-
"pools": [
|
|
55
|
-
{
|
|
56
|
-
"id": "pool-123",
|
|
57
|
-
"llama_apy_pct": 5.2,
|
|
58
|
-
"llama_tvl_usd": 1000000,
|
|
59
|
-
"llama_stablecoin": true,
|
|
60
|
-
"network": "base"
|
|
61
|
-
}
|
|
62
|
-
],
|
|
63
|
-
"total_found": 1,
|
|
64
|
-
"filters_applied": {
|
|
65
|
-
"min_apy": 0.03,
|
|
66
|
-
"min_tvl": 500000,
|
|
67
|
-
"stablecoin_only": true,
|
|
68
|
-
"network_codes": ["base", "ethereum"]
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
"get_pool_analytics": {
|
|
74
|
-
"description": "Get comprehensive analytics for specific pools",
|
|
75
|
-
"input": {
|
|
76
|
-
"pool_ids": ["pool-123"]
|
|
77
|
-
},
|
|
78
|
-
"output": {
|
|
79
|
-
"success": true,
|
|
80
|
-
"data": {
|
|
81
|
-
"analytics": [
|
|
82
|
-
{
|
|
83
|
-
"pool": {
|
|
84
|
-
"id": "pool-123",
|
|
85
|
-
"name": "USDC/USDT Pool",
|
|
86
|
-
"symbol": "USDC-USDT"
|
|
87
|
-
},
|
|
88
|
-
"llama_data": {
|
|
89
|
-
"llama_apy_pct": 5.2,
|
|
90
|
-
"llama_tvl_usd": 1000000
|
|
91
|
-
},
|
|
92
|
-
"combined_apy": 0.052,
|
|
93
|
-
"tvl_usd": 1000000
|
|
94
|
-
}
|
|
95
|
-
],
|
|
96
|
-
"total_pools": 1
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
42
|
}
|
|
100
43
|
}
|
|
@@ -69,77 +69,6 @@ class TestPoolAdapter:
|
|
|
69
69
|
assert success is True
|
|
70
70
|
assert data == mock_response
|
|
71
71
|
|
|
72
|
-
@pytest.mark.asyncio
|
|
73
|
-
async def test_find_high_yield_pools_success(self, adapter, mock_pool_client):
|
|
74
|
-
"""Test successful high yield pool discovery"""
|
|
75
|
-
mock_llama_response = {
|
|
76
|
-
"matches": [
|
|
77
|
-
{
|
|
78
|
-
"pool_id": "pool-123",
|
|
79
|
-
"llama_apy_pct": 5.2,
|
|
80
|
-
"llama_tvl_usd": 1000000,
|
|
81
|
-
"llama_stablecoin": True,
|
|
82
|
-
"network": "base",
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"pool_id": "pool-456",
|
|
86
|
-
"llama_apy_pct": 2.0,
|
|
87
|
-
"llama_tvl_usd": 500000,
|
|
88
|
-
"llama_stablecoin": True,
|
|
89
|
-
"network": "ethereum",
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"pool_id": "pool-789",
|
|
93
|
-
"llama_apy_pct": 6.0,
|
|
94
|
-
"llama_tvl_usd": 2000000,
|
|
95
|
-
"llama_stablecoin": False,
|
|
96
|
-
"network": "base",
|
|
97
|
-
},
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
mock_pool_client.get_llama_matches = AsyncMock(return_value=mock_llama_response)
|
|
101
|
-
|
|
102
|
-
success, data = await adapter.find_high_yield_pools(
|
|
103
|
-
min_apy=0.03, min_tvl=500000, stablecoin_only=True, network_codes=["base"]
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
assert success is True
|
|
107
|
-
assert len(data["pools"]) == 1 # Only pool-123 meets criteria
|
|
108
|
-
assert (
|
|
109
|
-
data["pools"][0].get("pool_id") == "pool-123"
|
|
110
|
-
or data["pools"][0].get("id") == "pool-123"
|
|
111
|
-
)
|
|
112
|
-
assert data["total_found"] == 1
|
|
113
|
-
assert data["filters_applied"]["min_apy"] == 0.03
|
|
114
|
-
assert data["filters_applied"]["stablecoin_only"] is True
|
|
115
|
-
|
|
116
|
-
@pytest.mark.asyncio
|
|
117
|
-
async def test_get_pool_analytics_success(self, adapter, mock_pool_client):
|
|
118
|
-
"""Test successful pool analytics generation"""
|
|
119
|
-
mock_pool_data = {
|
|
120
|
-
"pools": [
|
|
121
|
-
{"id": "pool-123", "name": "USDC/USDT Pool", "symbol": "USDC-USDT"}
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
mock_pool_client.get_pools_by_ids = AsyncMock(return_value=mock_pool_data)
|
|
125
|
-
|
|
126
|
-
mock_llama_data = {
|
|
127
|
-
"pool-123": {
|
|
128
|
-
"llama_apy_pct": 5.2,
|
|
129
|
-
"llama_combined_apy_pct": 5.2,
|
|
130
|
-
"llama_tvl_usd": 1000000,
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
mock_pool_client.get_llama_reports = AsyncMock(return_value=mock_llama_data)
|
|
134
|
-
|
|
135
|
-
success, data = await adapter.get_pool_analytics(["pool-123"])
|
|
136
|
-
|
|
137
|
-
assert success is True
|
|
138
|
-
assert len(data["analytics"]) == 1
|
|
139
|
-
assert data["analytics"][0]["pool"]["id"] == "pool-123"
|
|
140
|
-
assert round(data["analytics"][0]["combined_apy"], 6) == round(0.052, 6)
|
|
141
|
-
assert data["analytics"][0]["tvl_usd"] == 1000000
|
|
142
|
-
|
|
143
72
|
@pytest.mark.asyncio
|
|
144
73
|
async def test_get_pools_by_ids_failure(self, adapter, mock_pool_client):
|
|
145
74
|
"""Test pool retrieval failure"""
|
|
@@ -152,21 +81,6 @@ class TestPoolAdapter:
|
|
|
152
81
|
assert success is False
|
|
153
82
|
assert "API Error" in data
|
|
154
83
|
|
|
155
|
-
@pytest.mark.asyncio
|
|
156
|
-
async def test_find_high_yield_pools_no_matches(self, adapter, mock_pool_client):
|
|
157
|
-
"""Test high yield pool discovery with no matches"""
|
|
158
|
-
mock_llama_response = {"matches": []}
|
|
159
|
-
mock_pool_client.get_llama_matches.return_value = mock_llama_response
|
|
160
|
-
|
|
161
|
-
success, data = await adapter.find_high_yield_pools(
|
|
162
|
-
min_apy=0.10,
|
|
163
|
-
min_tvl=10000000,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
assert success is True
|
|
167
|
-
assert len(data["pools"]) == 0
|
|
168
|
-
assert data["total_found"] == 0
|
|
169
|
-
|
|
170
84
|
def test_adapter_type(self, adapter):
|
|
171
85
|
"""Test adapter has adapter_type"""
|
|
172
86
|
assert adapter.adapter_type == "POOL"
|
|
@@ -12,7 +12,7 @@ The adapter uses the TokenClient which automatically handles authentication and
|
|
|
12
12
|
|
|
13
13
|
The TokenClient will automatically:
|
|
14
14
|
- Use the WAYFINDER_API_URL from settings
|
|
15
|
-
- Handle authentication via
|
|
15
|
+
- Handle authentication via config.json
|
|
16
16
|
- Manage token refresh and retry logic
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
@@ -15,14 +15,10 @@ from wayfinder_paths.core.clients.protocols import (
|
|
|
15
15
|
HyperlendClientProtocol,
|
|
16
16
|
LedgerClientProtocol,
|
|
17
17
|
PoolClientProtocol,
|
|
18
|
-
SimulationClientProtocol,
|
|
19
18
|
TokenClientProtocol,
|
|
20
|
-
TransactionClientProtocol,
|
|
21
19
|
WalletClientProtocol,
|
|
22
20
|
)
|
|
23
|
-
from wayfinder_paths.core.clients.SimulationClient import SimulationClient
|
|
24
21
|
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
25
|
-
from wayfinder_paths.core.clients.TransactionClient import TransactionClient
|
|
26
22
|
from wayfinder_paths.core.clients.WalletClient import WalletClient
|
|
27
23
|
|
|
28
24
|
|
|
@@ -32,7 +28,7 @@ class ClientManager:
|
|
|
32
28
|
|
|
33
29
|
Args:
|
|
34
30
|
clients: Optional dict of pre-instantiated clients to inject directly.
|
|
35
|
-
Keys: 'token', 'hyperlend', 'ledger', 'wallet', 'transaction', 'pool', 'brap'
|
|
31
|
+
Keys: 'token', 'hyperlend', 'ledger', 'wallet', 'transaction', 'pool', 'brap'.
|
|
36
32
|
If not provided, defaults to HTTP-based clients.
|
|
37
33
|
skip_auth: If True, skips authentication (for SDK usage).
|
|
38
34
|
"""
|
|
@@ -59,12 +55,10 @@ class ClientManager:
|
|
|
59
55
|
self._auth_client: AuthClient | None = None
|
|
60
56
|
self._token_client: TokenClientProtocol | None = None
|
|
61
57
|
self._wallet_client: WalletClientProtocol | None = None
|
|
62
|
-
self._transaction_client: TransactionClientProtocol | None = None
|
|
63
58
|
self._ledger_client: LedgerClientProtocol | None = None
|
|
64
59
|
self._pool_client: PoolClientProtocol | None = None
|
|
65
60
|
self._hyperlend_client: HyperlendClientProtocol | None = None
|
|
66
61
|
self._brap_client: BRAPClientProtocol | None = None
|
|
67
|
-
self._simulation_client: SimulationClientProtocol | None = None
|
|
68
62
|
|
|
69
63
|
def _get_or_create_client(
|
|
70
64
|
self,
|
|
@@ -109,13 +103,6 @@ class ClientManager:
|
|
|
109
103
|
"""Get or create token client"""
|
|
110
104
|
return self._get_or_create_client("_token_client", "token", TokenClient)
|
|
111
105
|
|
|
112
|
-
@property
|
|
113
|
-
def transaction(self) -> TransactionClientProtocol:
|
|
114
|
-
"""Get or create transaction client"""
|
|
115
|
-
return self._get_or_create_client(
|
|
116
|
-
"_transaction_client", "transaction", TransactionClient
|
|
117
|
-
)
|
|
118
|
-
|
|
119
106
|
@property
|
|
120
107
|
def ledger(self) -> LedgerClientProtocol:
|
|
121
108
|
"""Get or create ledger client"""
|
|
@@ -143,13 +130,6 @@ class ClientManager:
|
|
|
143
130
|
"""Get or create BRAP client"""
|
|
144
131
|
return self._get_or_create_client("_brap_client", "brap", BRAPClient)
|
|
145
132
|
|
|
146
|
-
@property
|
|
147
|
-
def simulation(self) -> SimulationClientProtocol:
|
|
148
|
-
"""Get or create simulation client"""
|
|
149
|
-
return self._get_or_create_client(
|
|
150
|
-
"_simulation_client", "simulation", SimulationClient
|
|
151
|
-
)
|
|
152
|
-
|
|
153
133
|
async def authenticate(
|
|
154
134
|
self,
|
|
155
135
|
username: str | None = None,
|
|
@@ -206,5 +186,4 @@ class ClientManager:
|
|
|
206
186
|
"wallet": self._wallet_client,
|
|
207
187
|
"hyperlend": self._hyperlend_client,
|
|
208
188
|
"brap": self._brap_client,
|
|
209
|
-
"simulation": self._simulation_client,
|
|
210
189
|
}
|
|
@@ -33,14 +33,6 @@ class PoolBalance(TypedDict):
|
|
|
33
33
|
usd_value: NotRequired[float | None]
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class EnrichedBalances(TypedDict):
|
|
37
|
-
"""Enriched token balances response structure"""
|
|
38
|
-
|
|
39
|
-
wallet_address: Required[str]
|
|
40
|
-
balances: Required[list[TokenBalance]]
|
|
41
|
-
total_usd_value: NotRequired[float | None]
|
|
42
|
-
|
|
43
|
-
|
|
44
36
|
class WalletClient(WayfinderClient):
|
|
45
37
|
def __init__(self, api_key: str | None = None):
|
|
46
38
|
super().__init__(api_key=api_key)
|
|
@@ -121,13 +121,13 @@ class WayfinderClient:
|
|
|
121
121
|
|
|
122
122
|
async def _ensure_bearer_token(self) -> bool:
|
|
123
123
|
"""
|
|
124
|
-
Ensure Authorization header is set. Priority: existing header > constructor api_key > config.json api_key >
|
|
124
|
+
Ensure Authorization header is set. Priority: existing header > constructor api_key > config.json api_key > config.json tokens > username/password.
|
|
125
125
|
Raises PermissionError if no credentials found.
|
|
126
126
|
"""
|
|
127
127
|
if self.headers.get("Authorization"):
|
|
128
128
|
return True
|
|
129
129
|
|
|
130
|
-
# Check for API key: constructor > config.json
|
|
130
|
+
# Check for API key: constructor > config.json
|
|
131
131
|
api_key = self._api_key
|
|
132
132
|
if not api_key:
|
|
133
133
|
creds = self._load_config_credentials()
|
|
@@ -142,12 +142,7 @@ class WayfinderClient:
|
|
|
142
142
|
|
|
143
143
|
# Fall back to OAuth token-based auth
|
|
144
144
|
creds = self._load_config_credentials()
|
|
145
|
-
|
|
146
|
-
refresh = creds.get("refresh_token") or os.getenv("WAYFINDER_REFRESH_TOKEN")
|
|
147
|
-
|
|
148
|
-
if access:
|
|
149
|
-
self.set_tokens(access, refresh)
|
|
150
|
-
return True
|
|
145
|
+
refresh = creds.get("refresh_token")
|
|
151
146
|
|
|
152
147
|
if refresh:
|
|
153
148
|
self._refresh_token = refresh
|
|
@@ -155,8 +150,8 @@ class WayfinderClient:
|
|
|
155
150
|
if refreshed:
|
|
156
151
|
return True
|
|
157
152
|
|
|
158
|
-
username = creds.get("username")
|
|
159
|
-
password = creds.get("password")
|
|
153
|
+
username = creds.get("username")
|
|
154
|
+
password = creds.get("password")
|
|
160
155
|
|
|
161
156
|
if username and password:
|
|
162
157
|
try:
|
|
@@ -178,7 +173,7 @@ class WayfinderClient:
|
|
|
178
173
|
pass
|
|
179
174
|
|
|
180
175
|
raise PermissionError(
|
|
181
|
-
"Not authenticated: provide api_key (via constructor
|
|
176
|
+
"Not authenticated: provide api_key (via constructor or config.json) for service account auth, "
|
|
182
177
|
"or username+password/refresh_token in config.json for personal access"
|
|
183
178
|
)
|
|
184
179
|
|
|
@@ -201,7 +196,7 @@ class WayfinderClient:
|
|
|
201
196
|
# Ensure API key or bearer token is set in headers if available and not already set
|
|
202
197
|
# This ensures API keys are passed to all endpoints (including public ones) for rate limiting
|
|
203
198
|
if not self.headers.get("Authorization"):
|
|
204
|
-
# Try to get API key from constructor
|
|
199
|
+
# Try to get API key from constructor or config
|
|
205
200
|
api_key = self._api_key
|
|
206
201
|
if not api_key:
|
|
207
202
|
creds = self._load_config_credentials()
|
|
@@ -13,14 +13,10 @@ from wayfinder_paths.core.clients.protocols import (
|
|
|
13
13
|
HyperlendClientProtocol,
|
|
14
14
|
LedgerClientProtocol,
|
|
15
15
|
PoolClientProtocol,
|
|
16
|
-
SimulationClientProtocol,
|
|
17
16
|
TokenClientProtocol,
|
|
18
|
-
TransactionClientProtocol,
|
|
19
17
|
WalletClientProtocol,
|
|
20
18
|
)
|
|
21
|
-
from wayfinder_paths.core.clients.SimulationClient import SimulationClient
|
|
22
19
|
from wayfinder_paths.core.clients.TokenClient import TokenClient
|
|
23
|
-
from wayfinder_paths.core.clients.TransactionClient import TransactionClient
|
|
24
20
|
from wayfinder_paths.core.clients.WalletClient import WalletClient
|
|
25
21
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
26
22
|
|
|
@@ -30,19 +26,15 @@ __all__ = [
|
|
|
30
26
|
"AuthClient",
|
|
31
27
|
"TokenClient",
|
|
32
28
|
"WalletClient",
|
|
33
|
-
"TransactionClient",
|
|
34
29
|
"LedgerClient",
|
|
35
30
|
"PoolClient",
|
|
36
31
|
"BRAPClient",
|
|
37
|
-
"SimulationClient",
|
|
38
32
|
"HyperlendClient",
|
|
39
33
|
# Protocols for SDK usage
|
|
40
34
|
"TokenClientProtocol",
|
|
41
35
|
"HyperlendClientProtocol",
|
|
42
36
|
"LedgerClientProtocol",
|
|
43
37
|
"WalletClientProtocol",
|
|
44
|
-
"TransactionClientProtocol",
|
|
45
38
|
"PoolClientProtocol",
|
|
46
39
|
"BRAPClientProtocol",
|
|
47
|
-
"SimulationClientProtocol",
|
|
48
40
|
]
|
|
@@ -28,12 +28,10 @@ if TYPE_CHECKING:
|
|
|
28
28
|
LlamaReport,
|
|
29
29
|
PoolList,
|
|
30
30
|
)
|
|
31
|
-
from wayfinder_paths.core.clients.SimulationClient import SimulationResult
|
|
32
31
|
from wayfinder_paths.core.clients.TokenClient import (
|
|
33
32
|
GasToken,
|
|
34
33
|
TokenDetails,
|
|
35
34
|
)
|
|
36
|
-
from wayfinder_paths.core.clients.TransactionClient import TransactionPayload
|
|
37
35
|
from wayfinder_paths.core.clients.WalletClient import (
|
|
38
36
|
PoolBalance,
|
|
39
37
|
TokenBalance,
|
|
@@ -186,21 +184,6 @@ class WalletClientProtocol(Protocol):
|
|
|
186
184
|
...
|
|
187
185
|
|
|
188
186
|
|
|
189
|
-
class TransactionClientProtocol(Protocol):
|
|
190
|
-
"""Protocol for transaction operations"""
|
|
191
|
-
|
|
192
|
-
async def build_send(
|
|
193
|
-
self,
|
|
194
|
-
from_address: str,
|
|
195
|
-
to_address: str,
|
|
196
|
-
token_address: str,
|
|
197
|
-
amount: float,
|
|
198
|
-
chain_id: int,
|
|
199
|
-
) -> TransactionPayload:
|
|
200
|
-
"""Build a send transaction payload for EVM tokens/native transfers"""
|
|
201
|
-
...
|
|
202
|
-
|
|
203
|
-
|
|
204
187
|
class PoolClientProtocol(Protocol):
|
|
205
188
|
"""Protocol for pool-related read operations"""
|
|
206
189
|
|
|
@@ -242,49 +225,6 @@ class BRAPClientProtocol(Protocol):
|
|
|
242
225
|
...
|
|
243
226
|
|
|
244
227
|
|
|
245
|
-
class SimulationClientProtocol(Protocol):
|
|
246
|
-
"""Protocol for blockchain transaction simulations"""
|
|
247
|
-
|
|
248
|
-
async def simulate_send(
|
|
249
|
-
self,
|
|
250
|
-
from_address: str,
|
|
251
|
-
to_address: str,
|
|
252
|
-
token_address: str,
|
|
253
|
-
amount: str,
|
|
254
|
-
chain_id: int,
|
|
255
|
-
initial_balances: dict[str, str],
|
|
256
|
-
) -> SimulationResult:
|
|
257
|
-
"""Simulate sending native ETH or ERC20 tokens"""
|
|
258
|
-
...
|
|
259
|
-
|
|
260
|
-
async def simulate_approve(
|
|
261
|
-
self,
|
|
262
|
-
from_address: str,
|
|
263
|
-
to_address: str,
|
|
264
|
-
token_address: str,
|
|
265
|
-
amount: str,
|
|
266
|
-
chain_id: int,
|
|
267
|
-
initial_balances: dict[str, str],
|
|
268
|
-
clear_approval_first: bool = False,
|
|
269
|
-
) -> SimulationResult:
|
|
270
|
-
"""Simulate ERC20 token approval"""
|
|
271
|
-
...
|
|
272
|
-
|
|
273
|
-
async def simulate_swap(
|
|
274
|
-
self,
|
|
275
|
-
from_token_address: str,
|
|
276
|
-
to_token_address: str,
|
|
277
|
-
from_chain_id: int,
|
|
278
|
-
to_chain_id: int,
|
|
279
|
-
amount: str,
|
|
280
|
-
from_address: str,
|
|
281
|
-
slippage: float,
|
|
282
|
-
initial_balances: dict[str, str],
|
|
283
|
-
) -> SimulationResult:
|
|
284
|
-
"""Simulate token swap operation"""
|
|
285
|
-
...
|
|
286
|
-
|
|
287
|
-
|
|
288
228
|
class HyperliquidExecutorProtocol(Protocol):
|
|
289
229
|
"""Protocol for Hyperliquid order execution operations."""
|
|
290
230
|
|