wayfinder-paths 0.1.4__py3-none-any.whl → 0.1.5__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 +14 -14
- wayfinder_paths/__init__.py +3 -3
- wayfinder_paths/adapters/balance_adapter/README.md +10 -10
- wayfinder_paths/adapters/balance_adapter/adapter.py +10 -9
- wayfinder_paths/adapters/balance_adapter/examples.json +1 -1
- wayfinder_paths/adapters/brap_adapter/README.md +1 -1
- wayfinder_paths/adapters/brap_adapter/adapter.py +28 -21
- wayfinder_paths/adapters/hyperlend_adapter/adapter.py +33 -26
- wayfinder_paths/adapters/ledger_adapter/README.md +26 -39
- wayfinder_paths/adapters/ledger_adapter/adapter.py +78 -75
- wayfinder_paths/adapters/ledger_adapter/examples.json +10 -4
- wayfinder_paths/adapters/ledger_adapter/manifest.yaml +4 -4
- wayfinder_paths/adapters/ledger_adapter/test_adapter.py +31 -26
- wayfinder_paths/adapters/pool_adapter/README.md +1 -13
- wayfinder_paths/adapters/pool_adapter/adapter.py +12 -19
- wayfinder_paths/adapters/token_adapter/adapter.py +8 -4
- wayfinder_paths/core/__init__.py +3 -3
- wayfinder_paths/core/adapters/BaseAdapter.py +20 -3
- wayfinder_paths/core/adapters/models.py +41 -0
- wayfinder_paths/core/clients/BRAPClient.py +21 -2
- wayfinder_paths/core/clients/ClientManager.py +42 -63
- wayfinder_paths/core/clients/HyperlendClient.py +46 -5
- wayfinder_paths/core/clients/LedgerClient.py +350 -124
- wayfinder_paths/core/clients/PoolClient.py +51 -19
- wayfinder_paths/core/clients/SimulationClient.py +16 -4
- wayfinder_paths/core/clients/TokenClient.py +34 -18
- wayfinder_paths/core/clients/TransactionClient.py +18 -2
- wayfinder_paths/core/clients/WalletClient.py +35 -4
- wayfinder_paths/core/clients/WayfinderClient.py +16 -5
- wayfinder_paths/core/clients/protocols.py +69 -62
- wayfinder_paths/core/clients/sdk_example.py +0 -5
- wayfinder_paths/core/config.py +192 -103
- wayfinder_paths/core/constants/base.py +17 -0
- wayfinder_paths/core/engine/{VaultJob.py → StrategyJob.py} +25 -19
- wayfinder_paths/core/engine/__init__.py +2 -2
- wayfinder_paths/core/services/base.py +6 -4
- wayfinder_paths/core/services/local_evm_txn.py +3 -2
- wayfinder_paths/core/settings.py +2 -2
- wayfinder_paths/core/strategies/Strategy.py +123 -37
- wayfinder_paths/core/utils/evm_helpers.py +12 -10
- wayfinder_paths/core/wallets/README.md +3 -3
- wayfinder_paths/core/wallets/WalletManager.py +3 -3
- wayfinder_paths/run_strategy.py +26 -24
- wayfinder_paths/scripts/make_wallets.py +6 -6
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/README.md +6 -6
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/strategy.py +36 -156
- wayfinder_paths/strategies/hyperlend_stable_yield_strategy/test_strategy.py +6 -6
- wayfinder_paths/strategies/stablecoin_yield_strategy/README.md +11 -11
- wayfinder_paths/strategies/stablecoin_yield_strategy/manifest.yaml +1 -1
- wayfinder_paths/strategies/stablecoin_yield_strategy/strategy.py +92 -92
- wayfinder_paths/strategies/stablecoin_yield_strategy/test_strategy.py +6 -6
- wayfinder_paths/templates/adapter/README.md +1 -1
- wayfinder_paths/templates/adapter/test_adapter.py +1 -1
- wayfinder_paths/templates/strategy/README.md +4 -4
- wayfinder_paths/templates/strategy/test_strategy.py +7 -7
- wayfinder_paths/tests/test_test_coverage.py +5 -5
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/METADATA +46 -47
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/RECORD +60 -59
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/LICENSE +0 -0
- {wayfinder_paths-0.1.4.dist-info → wayfinder_paths-0.1.5.dist-info}/WHEEL +0 -0
|
@@ -3,14 +3,26 @@ Simulation Client
|
|
|
3
3
|
Handles blockchain transaction simulations via Gorlami/Tenderly
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import time
|
|
7
|
-
from typing import
|
|
9
|
+
from typing import NotRequired, Required, TypedDict
|
|
8
10
|
|
|
9
11
|
from loguru import logger
|
|
10
12
|
|
|
11
13
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
class SimulationResult(TypedDict):
|
|
17
|
+
"""Simulation result structure"""
|
|
18
|
+
|
|
19
|
+
success: Required[bool]
|
|
20
|
+
gas_used: NotRequired[str | None]
|
|
21
|
+
gas_price: NotRequired[str | None]
|
|
22
|
+
balances: NotRequired[dict[str, str]]
|
|
23
|
+
error: NotRequired[str | None]
|
|
24
|
+
|
|
25
|
+
|
|
14
26
|
class SimulationClient(WayfinderClient):
|
|
15
27
|
"""Client for blockchain transaction simulations"""
|
|
16
28
|
|
|
@@ -25,7 +37,7 @@ class SimulationClient(WayfinderClient):
|
|
|
25
37
|
amount: str,
|
|
26
38
|
chain_id: int,
|
|
27
39
|
initial_balances: dict[str, str],
|
|
28
|
-
) ->
|
|
40
|
+
) -> SimulationResult:
|
|
29
41
|
"""
|
|
30
42
|
Simulate sending native ETH or ERC20 tokens.
|
|
31
43
|
|
|
@@ -77,7 +89,7 @@ class SimulationClient(WayfinderClient):
|
|
|
77
89
|
chain_id: int,
|
|
78
90
|
initial_balances: dict[str, str],
|
|
79
91
|
clear_approval_first: bool = False,
|
|
80
|
-
) ->
|
|
92
|
+
) -> SimulationResult:
|
|
81
93
|
"""
|
|
82
94
|
Simulate ERC20 token approval.
|
|
83
95
|
|
|
@@ -132,7 +144,7 @@ class SimulationClient(WayfinderClient):
|
|
|
132
144
|
from_address: str,
|
|
133
145
|
slippage: float,
|
|
134
146
|
initial_balances: dict[str, str],
|
|
135
|
-
) ->
|
|
147
|
+
) -> SimulationResult:
|
|
136
148
|
"""
|
|
137
149
|
Simulate token swap operation.
|
|
138
150
|
|
|
@@ -3,13 +3,44 @@ Token Adapter
|
|
|
3
3
|
Handles token information, prices, and parsing
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import NotRequired, Required, TypedDict
|
|
7
9
|
|
|
8
10
|
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
9
11
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
10
12
|
from wayfinder_paths.core.settings import settings
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
class TokenDetails(TypedDict):
|
|
16
|
+
"""Token details response structure"""
|
|
17
|
+
|
|
18
|
+
id: Required[str]
|
|
19
|
+
address: Required[str]
|
|
20
|
+
symbol: Required[str]
|
|
21
|
+
name: Required[str]
|
|
22
|
+
decimals: Required[int]
|
|
23
|
+
chain_id: Required[int]
|
|
24
|
+
chain_code: Required[str]
|
|
25
|
+
price_usd: NotRequired[float]
|
|
26
|
+
price: NotRequired[float]
|
|
27
|
+
image_url: NotRequired[str | None]
|
|
28
|
+
coingecko_id: NotRequired[str | None]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class GasToken(TypedDict):
|
|
32
|
+
"""Gas token response structure"""
|
|
33
|
+
|
|
34
|
+
id: Required[str]
|
|
35
|
+
address: Required[str]
|
|
36
|
+
symbol: Required[str]
|
|
37
|
+
name: Required[str]
|
|
38
|
+
decimals: Required[int]
|
|
39
|
+
chain_id: Required[int]
|
|
40
|
+
chain_code: Required[str]
|
|
41
|
+
price_usd: NotRequired[float]
|
|
42
|
+
|
|
43
|
+
|
|
13
44
|
class TokenClient(WayfinderClient):
|
|
14
45
|
"""Adapter for token-related operations"""
|
|
15
46
|
|
|
@@ -22,7 +53,7 @@ class TokenClient(WayfinderClient):
|
|
|
22
53
|
|
|
23
54
|
async def get_token_details(
|
|
24
55
|
self, token_id: str, force_refresh: bool = False
|
|
25
|
-
) ->
|
|
56
|
+
) -> TokenDetails:
|
|
26
57
|
"""
|
|
27
58
|
Get token data including price from the token-details endpoint
|
|
28
59
|
|
|
@@ -44,7 +75,7 @@ class TokenClient(WayfinderClient):
|
|
|
44
75
|
data = response.json()
|
|
45
76
|
return data.get("data", data)
|
|
46
77
|
|
|
47
|
-
async def get_gas_token(self, chain_code: str) ->
|
|
78
|
+
async def get_gas_token(self, chain_code: str) -> GasToken:
|
|
48
79
|
"""
|
|
49
80
|
Fetch the native gas token for a given chain code via public endpoint.
|
|
50
81
|
Example: GET /api/v1/public/tokens/gas/?chain_code=base
|
|
@@ -56,18 +87,3 @@ class TokenClient(WayfinderClient):
|
|
|
56
87
|
response.raise_for_status()
|
|
57
88
|
data = response.json()
|
|
58
89
|
return data.get("data", data)
|
|
59
|
-
|
|
60
|
-
async def is_native_token(
|
|
61
|
-
self, token_address: str, chain_id: int
|
|
62
|
-
) -> dict[str, Any]:
|
|
63
|
-
"""
|
|
64
|
-
Determine if a token address corresponds to the native gas token on a chain.
|
|
65
|
-
Returns the API payload (usually includes an `is_native` boolean).
|
|
66
|
-
"""
|
|
67
|
-
url = f"{settings.WAYFINDER_API_URL}/public/tokens/is-native/"
|
|
68
|
-
params = {"token_address": token_address, "chain_id": str(chain_id)}
|
|
69
|
-
# Public endpoint: do not pass auth headers
|
|
70
|
-
response = await self._request("GET", url, params=params, headers={})
|
|
71
|
-
response.raise_for_status()
|
|
72
|
-
data = response.json()
|
|
73
|
-
return data.get("data", data)
|
|
@@ -3,13 +3,29 @@ Transaction Client
|
|
|
3
3
|
Handles transaction building, gas estimation, and monitoring
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import NotRequired, Required, TypedDict
|
|
7
9
|
|
|
8
10
|
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
9
11
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
10
12
|
from wayfinder_paths.core.settings import settings
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
class TransactionPayload(TypedDict):
|
|
16
|
+
"""Transaction payload structure"""
|
|
17
|
+
|
|
18
|
+
from_address: Required[str]
|
|
19
|
+
to_address: Required[str]
|
|
20
|
+
token_address: Required[str]
|
|
21
|
+
amount: Required[str]
|
|
22
|
+
chain_id: Required[int]
|
|
23
|
+
data: NotRequired[str | None]
|
|
24
|
+
value: NotRequired[str | None]
|
|
25
|
+
gas_limit: NotRequired[str | None]
|
|
26
|
+
gas_price: NotRequired[str | None]
|
|
27
|
+
|
|
28
|
+
|
|
13
29
|
class TransactionClient(WayfinderClient):
|
|
14
30
|
"""Client for transaction operations"""
|
|
15
31
|
|
|
@@ -27,7 +43,7 @@ class TransactionClient(WayfinderClient):
|
|
|
27
43
|
token_address: str,
|
|
28
44
|
amount: float,
|
|
29
45
|
chain_id: int,
|
|
30
|
-
) ->
|
|
46
|
+
) -> TransactionPayload:
|
|
31
47
|
"""
|
|
32
48
|
Build a send transaction payload for EVM tokens/native transfers.
|
|
33
49
|
|
|
@@ -3,13 +3,44 @@ Wallet Client
|
|
|
3
3
|
Fetches wallet-related data such as aggregated balances for the authenticated user.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import NotRequired, Required, TypedDict
|
|
7
9
|
|
|
8
10
|
from wayfinder_paths.core.clients.AuthClient import AuthClient
|
|
9
11
|
from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
|
|
10
12
|
from wayfinder_paths.core.settings import settings
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
class TokenBalance(TypedDict):
|
|
16
|
+
"""Token balance response structure"""
|
|
17
|
+
|
|
18
|
+
token_id: Required[str]
|
|
19
|
+
wallet_address: Required[str]
|
|
20
|
+
balance: Required[str]
|
|
21
|
+
balance_human: NotRequired[float | None]
|
|
22
|
+
usd_value: NotRequired[float | None]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PoolBalance(TypedDict):
|
|
26
|
+
"""Pool balance response structure"""
|
|
27
|
+
|
|
28
|
+
pool_address: Required[str]
|
|
29
|
+
chain_id: Required[int]
|
|
30
|
+
user_address: Required[str]
|
|
31
|
+
balance: Required[str]
|
|
32
|
+
balance_human: NotRequired[float | None]
|
|
33
|
+
usd_value: NotRequired[float | None]
|
|
34
|
+
|
|
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
|
+
|
|
13
44
|
class WalletClient(WayfinderClient):
|
|
14
45
|
def __init__(self, api_key: str | None = None):
|
|
15
46
|
super().__init__(api_key=api_key)
|
|
@@ -22,7 +53,7 @@ class WalletClient(WayfinderClient):
|
|
|
22
53
|
token_id: str,
|
|
23
54
|
wallet_address: str,
|
|
24
55
|
human_readable: bool = True,
|
|
25
|
-
) ->
|
|
56
|
+
) -> TokenBalance:
|
|
26
57
|
"""
|
|
27
58
|
Fetch a single token balance for an explicit wallet address.
|
|
28
59
|
|
|
@@ -45,7 +76,7 @@ class WalletClient(WayfinderClient):
|
|
|
45
76
|
chain_id: int,
|
|
46
77
|
user_address: str,
|
|
47
78
|
human_readable: bool = True,
|
|
48
|
-
) ->
|
|
79
|
+
) -> PoolBalance:
|
|
49
80
|
"""
|
|
50
81
|
Fetch a wallet's LP/share balance for a given pool address and chain.
|
|
51
82
|
|
|
@@ -69,7 +100,7 @@ class WalletClient(WayfinderClient):
|
|
|
69
100
|
enrich: bool = True,
|
|
70
101
|
from_cache: bool = False,
|
|
71
102
|
add_llama: bool = True,
|
|
72
|
-
) ->
|
|
103
|
+
) -> EnrichedBalances:
|
|
73
104
|
"""
|
|
74
105
|
Fetch all token balances for a wallet with enrichment via the enriched endpoint.
|
|
75
106
|
|
|
@@ -6,6 +6,7 @@ from typing import Any
|
|
|
6
6
|
import httpx
|
|
7
7
|
from loguru import logger
|
|
8
8
|
|
|
9
|
+
from wayfinder_paths.core.constants.base import DEFAULT_HTTP_TIMEOUT
|
|
9
10
|
from wayfinder_paths.core.settings import settings
|
|
10
11
|
|
|
11
12
|
|
|
@@ -19,7 +20,7 @@ class WayfinderClient:
|
|
|
19
20
|
If provided, uses API key auth. Otherwise falls back to config.json.
|
|
20
21
|
"""
|
|
21
22
|
self.api_base_url = f"{settings.WAYFINDER_API_URL}/"
|
|
22
|
-
timeout = httpx.Timeout(
|
|
23
|
+
timeout = httpx.Timeout(DEFAULT_HTTP_TIMEOUT)
|
|
23
24
|
self.client = httpx.AsyncClient(timeout=timeout)
|
|
24
25
|
|
|
25
26
|
self.headers = {
|
|
@@ -101,7 +102,16 @@ class WayfinderClient:
|
|
|
101
102
|
"refresh_token": user.get("refresh_token"),
|
|
102
103
|
"api_key": user.get("api_key") or system.get("api_key"),
|
|
103
104
|
}
|
|
104
|
-
except
|
|
105
|
+
except (FileNotFoundError, json.JSONDecodeError, OSError) as e:
|
|
106
|
+
logger.debug(f"Could not load config file at {path}: {e}")
|
|
107
|
+
return {
|
|
108
|
+
"username": None,
|
|
109
|
+
"password": None,
|
|
110
|
+
"refresh_token": None,
|
|
111
|
+
"api_key": None,
|
|
112
|
+
}
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.warning(f"Unexpected error loading config file at {path}: {e}")
|
|
105
115
|
return {
|
|
106
116
|
"username": None,
|
|
107
117
|
"password": None,
|
|
@@ -161,9 +171,10 @@ class WayfinderClient:
|
|
|
161
171
|
data = response.json()
|
|
162
172
|
access = data.get("access") or data.get("access_token")
|
|
163
173
|
refresh = data.get("refresh") or data.get("refresh_token")
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
except Exception:
|
|
174
|
+
self.set_tokens(access, refresh)
|
|
175
|
+
return bool(access)
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.debug(f"Failed to authenticate with username/password: {e}")
|
|
167
178
|
pass
|
|
168
179
|
|
|
169
180
|
raise PermissionError(
|
|
@@ -7,7 +7,39 @@ When used as an SDK, users can provide custom implementations that match these p
|
|
|
7
7
|
Note: AuthClient is excluded as SDK users handle their own authentication.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Protocol
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from wayfinder_paths.core.clients.BRAPClient import BRAPQuote
|
|
16
|
+
from wayfinder_paths.core.clients.HyperlendClient import (
|
|
17
|
+
AssetsView,
|
|
18
|
+
LendRateHistory,
|
|
19
|
+
MarketEntry,
|
|
20
|
+
StableMarket,
|
|
21
|
+
)
|
|
22
|
+
from wayfinder_paths.core.clients.LedgerClient import (
|
|
23
|
+
NetDeposit,
|
|
24
|
+
StrategyTransactionList,
|
|
25
|
+
TransactionRecord,
|
|
26
|
+
)
|
|
27
|
+
from wayfinder_paths.core.clients.PoolClient import (
|
|
28
|
+
LlamaMatch,
|
|
29
|
+
LlamaReport,
|
|
30
|
+
PoolList,
|
|
31
|
+
)
|
|
32
|
+
from wayfinder_paths.core.clients.SimulationClient import SimulationResult
|
|
33
|
+
from wayfinder_paths.core.clients.TokenClient import (
|
|
34
|
+
GasToken,
|
|
35
|
+
TokenDetails,
|
|
36
|
+
)
|
|
37
|
+
from wayfinder_paths.core.clients.TransactionClient import TransactionPayload
|
|
38
|
+
from wayfinder_paths.core.clients.WalletClient import (
|
|
39
|
+
EnrichedBalances,
|
|
40
|
+
PoolBalance,
|
|
41
|
+
TokenBalance,
|
|
42
|
+
)
|
|
11
43
|
|
|
12
44
|
|
|
13
45
|
class TokenClientProtocol(Protocol):
|
|
@@ -15,20 +47,14 @@ class TokenClientProtocol(Protocol):
|
|
|
15
47
|
|
|
16
48
|
async def get_token_details(
|
|
17
49
|
self, token_id: str, force_refresh: bool = False
|
|
18
|
-
) ->
|
|
50
|
+
) -> TokenDetails:
|
|
19
51
|
"""Get token data including price from the token-details endpoint"""
|
|
20
52
|
...
|
|
21
53
|
|
|
22
|
-
async def get_gas_token(self, chain_code: str) ->
|
|
54
|
+
async def get_gas_token(self, chain_code: str) -> GasToken:
|
|
23
55
|
"""Fetch the native gas token for a given chain code"""
|
|
24
56
|
...
|
|
25
57
|
|
|
26
|
-
async def is_native_token(
|
|
27
|
-
self, token_address: str, chain_id: int
|
|
28
|
-
) -> dict[str, Any]:
|
|
29
|
-
"""Determine if a token address corresponds to the native gas token on a chain"""
|
|
30
|
-
...
|
|
31
|
-
|
|
32
58
|
|
|
33
59
|
class HyperlendClientProtocol(Protocol):
|
|
34
60
|
"""Protocol for Hyperlend-related operations"""
|
|
@@ -41,7 +67,7 @@ class HyperlendClientProtocol(Protocol):
|
|
|
41
67
|
buffer_bps: int | None = None,
|
|
42
68
|
min_buffer_tokens: float | None = None,
|
|
43
69
|
is_stable_symbol: bool | None = None,
|
|
44
|
-
) ->
|
|
70
|
+
) -> list[StableMarket]:
|
|
45
71
|
"""Fetch stable markets from Hyperlend"""
|
|
46
72
|
...
|
|
47
73
|
|
|
@@ -50,7 +76,7 @@ class HyperlendClientProtocol(Protocol):
|
|
|
50
76
|
*,
|
|
51
77
|
chain_id: int,
|
|
52
78
|
user_address: str,
|
|
53
|
-
) ->
|
|
79
|
+
) -> AssetsView:
|
|
54
80
|
"""Fetch assets view for a user address from Hyperlend"""
|
|
55
81
|
...
|
|
56
82
|
|
|
@@ -59,7 +85,7 @@ class HyperlendClientProtocol(Protocol):
|
|
|
59
85
|
*,
|
|
60
86
|
chain_id: int,
|
|
61
87
|
token_address: str,
|
|
62
|
-
) ->
|
|
88
|
+
) -> MarketEntry:
|
|
63
89
|
"""Fetch market entry from Hyperlend"""
|
|
64
90
|
...
|
|
65
91
|
|
|
@@ -69,35 +95,35 @@ class HyperlendClientProtocol(Protocol):
|
|
|
69
95
|
chain_id: int,
|
|
70
96
|
token_address: str,
|
|
71
97
|
lookback_hours: int,
|
|
72
|
-
) ->
|
|
98
|
+
) -> LendRateHistory:
|
|
73
99
|
"""Fetch lend rate history from Hyperlend"""
|
|
74
100
|
...
|
|
75
101
|
|
|
76
102
|
|
|
77
103
|
class LedgerClientProtocol(Protocol):
|
|
78
|
-
"""Protocol for
|
|
104
|
+
"""Protocol for strategy transaction history and bookkeeping operations"""
|
|
79
105
|
|
|
80
|
-
async def
|
|
106
|
+
async def get_strategy_transactions(
|
|
81
107
|
self,
|
|
82
108
|
*,
|
|
83
109
|
wallet_address: str,
|
|
84
110
|
limit: int = 50,
|
|
85
111
|
offset: int = 0,
|
|
86
|
-
) ->
|
|
87
|
-
"""Fetch a paginated list of transactions for a given
|
|
112
|
+
) -> StrategyTransactionList:
|
|
113
|
+
"""Fetch a paginated list of transactions for a given strategy wallet"""
|
|
88
114
|
...
|
|
89
115
|
|
|
90
|
-
async def
|
|
91
|
-
"""Fetch the net deposit (deposits - withdrawals) for a
|
|
116
|
+
async def get_strategy_net_deposit(self, *, wallet_address: str) -> NetDeposit:
|
|
117
|
+
"""Fetch the net deposit (deposits - withdrawals) for a strategy"""
|
|
92
118
|
...
|
|
93
119
|
|
|
94
|
-
async def
|
|
120
|
+
async def get_strategy_latest_transactions(
|
|
95
121
|
self, *, wallet_address: str
|
|
96
|
-
) ->
|
|
97
|
-
"""Fetch the latest transactions for a
|
|
122
|
+
) -> StrategyTransactionList:
|
|
123
|
+
"""Fetch the latest transactions for a strategy"""
|
|
98
124
|
...
|
|
99
125
|
|
|
100
|
-
async def
|
|
126
|
+
async def add_strategy_deposit(
|
|
101
127
|
self,
|
|
102
128
|
*,
|
|
103
129
|
wallet_address: str,
|
|
@@ -107,11 +133,11 @@ class LedgerClientProtocol(Protocol):
|
|
|
107
133
|
usd_value: str | float,
|
|
108
134
|
data: dict[str, Any] | None = None,
|
|
109
135
|
strategy_name: str | None = None,
|
|
110
|
-
) ->
|
|
111
|
-
"""Record a deposit for a
|
|
136
|
+
) -> TransactionRecord:
|
|
137
|
+
"""Record a deposit for a strategy"""
|
|
112
138
|
...
|
|
113
139
|
|
|
114
|
-
async def
|
|
140
|
+
async def add_strategy_withdraw(
|
|
115
141
|
self,
|
|
116
142
|
*,
|
|
117
143
|
wallet_address: str,
|
|
@@ -121,32 +147,19 @@ class LedgerClientProtocol(Protocol):
|
|
|
121
147
|
usd_value: str | float,
|
|
122
148
|
data: dict[str, Any] | None = None,
|
|
123
149
|
strategy_name: str | None = None,
|
|
124
|
-
) ->
|
|
125
|
-
"""Record a withdrawal for a
|
|
150
|
+
) -> TransactionRecord:
|
|
151
|
+
"""Record a withdrawal for a strategy"""
|
|
126
152
|
...
|
|
127
153
|
|
|
128
|
-
async def
|
|
154
|
+
async def add_strategy_operation(
|
|
129
155
|
self,
|
|
130
156
|
*,
|
|
131
157
|
wallet_address: str,
|
|
132
158
|
operation_data: dict[str, Any],
|
|
133
159
|
usd_value: str | float,
|
|
134
160
|
strategy_name: str | None = None,
|
|
135
|
-
) ->
|
|
136
|
-
"""Record a
|
|
137
|
-
...
|
|
138
|
-
|
|
139
|
-
async def add_vault_cashflow(
|
|
140
|
-
self,
|
|
141
|
-
*,
|
|
142
|
-
wallet_address: str,
|
|
143
|
-
block_timestamp: int,
|
|
144
|
-
token_addr: str,
|
|
145
|
-
amount: str | int | float,
|
|
146
|
-
description: str,
|
|
147
|
-
strategy_name: str | None = None,
|
|
148
|
-
) -> dict[str, Any]:
|
|
149
|
-
"""Record a cashflow for a vault (interest, funding, reward, or fee)"""
|
|
161
|
+
) -> TransactionRecord:
|
|
162
|
+
"""Record a strategy operation (e.g., swaps, rebalances)"""
|
|
150
163
|
...
|
|
151
164
|
|
|
152
165
|
|
|
@@ -159,7 +172,7 @@ class WalletClientProtocol(Protocol):
|
|
|
159
172
|
token_id: str,
|
|
160
173
|
wallet_address: str,
|
|
161
174
|
human_readable: bool = True,
|
|
162
|
-
) ->
|
|
175
|
+
) -> TokenBalance:
|
|
163
176
|
"""Fetch a single token balance for an explicit wallet address"""
|
|
164
177
|
...
|
|
165
178
|
|
|
@@ -170,7 +183,7 @@ class WalletClientProtocol(Protocol):
|
|
|
170
183
|
chain_id: int,
|
|
171
184
|
user_address: str,
|
|
172
185
|
human_readable: bool = True,
|
|
173
|
-
) ->
|
|
186
|
+
) -> PoolBalance:
|
|
174
187
|
"""Fetch a wallet's LP/share balance for a given pool address and chain"""
|
|
175
188
|
...
|
|
176
189
|
|
|
@@ -181,7 +194,7 @@ class WalletClientProtocol(Protocol):
|
|
|
181
194
|
enrich: bool = True,
|
|
182
195
|
from_cache: bool = False,
|
|
183
196
|
add_llama: bool = True,
|
|
184
|
-
) ->
|
|
197
|
+
) -> EnrichedBalances:
|
|
185
198
|
"""Fetch all token balances for a wallet with enrichment"""
|
|
186
199
|
...
|
|
187
200
|
|
|
@@ -196,7 +209,7 @@ class TransactionClientProtocol(Protocol):
|
|
|
196
209
|
token_address: str,
|
|
197
210
|
amount: float,
|
|
198
211
|
chain_id: int,
|
|
199
|
-
) ->
|
|
212
|
+
) -> TransactionPayload:
|
|
200
213
|
"""Build a send transaction payload for EVM tokens/native transfers"""
|
|
201
214
|
...
|
|
202
215
|
|
|
@@ -209,25 +222,19 @@ class PoolClientProtocol(Protocol):
|
|
|
209
222
|
*,
|
|
210
223
|
pool_ids: str,
|
|
211
224
|
merge_external: bool | None = None,
|
|
212
|
-
) ->
|
|
225
|
+
) -> PoolList:
|
|
213
226
|
"""Fetch pools by comma-separated pool ids"""
|
|
214
227
|
...
|
|
215
228
|
|
|
216
|
-
async def get_all_pools(
|
|
217
|
-
self, *, merge_external: bool | None = None
|
|
218
|
-
) -> dict[str, Any]:
|
|
229
|
+
async def get_all_pools(self, *, merge_external: bool | None = None) -> PoolList:
|
|
219
230
|
"""Fetch all pools"""
|
|
220
231
|
...
|
|
221
232
|
|
|
222
|
-
async def
|
|
223
|
-
"""Fetch combined pool reports"""
|
|
224
|
-
...
|
|
225
|
-
|
|
226
|
-
async def get_llama_matches(self) -> dict[str, Any]:
|
|
233
|
+
async def get_llama_matches(self) -> dict[str, LlamaMatch]:
|
|
227
234
|
"""Fetch Llama matches for pools"""
|
|
228
235
|
...
|
|
229
236
|
|
|
230
|
-
async def get_llama_reports(self, *, identifiers: str) -> dict[str,
|
|
237
|
+
async def get_llama_reports(self, *, identifiers: str) -> dict[str, LlamaReport]:
|
|
231
238
|
"""Fetch Llama reports using identifiers"""
|
|
232
239
|
...
|
|
233
240
|
|
|
@@ -247,7 +254,7 @@ class BRAPClientProtocol(Protocol):
|
|
|
247
254
|
amount1: str,
|
|
248
255
|
slippage: float | None = None,
|
|
249
256
|
wayfinder_fee: float | None = None,
|
|
250
|
-
) ->
|
|
257
|
+
) -> BRAPQuote:
|
|
251
258
|
"""Get a quote for a bridge/swap operation"""
|
|
252
259
|
...
|
|
253
260
|
|
|
@@ -263,7 +270,7 @@ class SimulationClientProtocol(Protocol):
|
|
|
263
270
|
amount: str,
|
|
264
271
|
chain_id: int,
|
|
265
272
|
initial_balances: dict[str, str],
|
|
266
|
-
) ->
|
|
273
|
+
) -> SimulationResult:
|
|
267
274
|
"""Simulate sending native ETH or ERC20 tokens"""
|
|
268
275
|
...
|
|
269
276
|
|
|
@@ -276,7 +283,7 @@ class SimulationClientProtocol(Protocol):
|
|
|
276
283
|
chain_id: int,
|
|
277
284
|
initial_balances: dict[str, str],
|
|
278
285
|
clear_approval_first: bool = False,
|
|
279
|
-
) ->
|
|
286
|
+
) -> SimulationResult:
|
|
280
287
|
"""Simulate ERC20 token approval"""
|
|
281
288
|
...
|
|
282
289
|
|
|
@@ -290,6 +297,6 @@ class SimulationClientProtocol(Protocol):
|
|
|
290
297
|
from_address: str,
|
|
291
298
|
slippage: float,
|
|
292
299
|
initial_balances: dict[str, str],
|
|
293
|
-
) ->
|
|
300
|
+
) -> SimulationResult:
|
|
294
301
|
"""Simulate token swap operation"""
|
|
295
302
|
...
|
|
@@ -31,11 +31,6 @@ class CachedTokenClient:
|
|
|
31
31
|
async def get_gas_token(self, chain_code: str) -> dict[str, Any]:
|
|
32
32
|
return await self._default_client.get_gas_token(chain_code)
|
|
33
33
|
|
|
34
|
-
async def is_native_token(
|
|
35
|
-
self, token_address: str, chain_id: int
|
|
36
|
-
) -> dict[str, Any]:
|
|
37
|
-
return await self._default_client.is_native_token(token_address, chain_id)
|
|
38
|
-
|
|
39
34
|
|
|
40
35
|
class MockHyperlendClient:
|
|
41
36
|
"""Mock client for testing"""
|