wayfinder-paths 0.1.1__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 (115) hide show
  1. wayfinder_paths/CONFIG_GUIDE.md +394 -0
  2. wayfinder_paths/__init__.py +21 -0
  3. wayfinder_paths/config.example.json +20 -0
  4. wayfinder_paths/conftest.py +31 -0
  5. wayfinder_paths/core/__init__.py +13 -0
  6. wayfinder_paths/core/adapters/BaseAdapter.py +48 -0
  7. wayfinder_paths/core/adapters/__init__.py +5 -0
  8. wayfinder_paths/core/adapters/base.py +5 -0
  9. wayfinder_paths/core/clients/AuthClient.py +83 -0
  10. wayfinder_paths/core/clients/BRAPClient.py +90 -0
  11. wayfinder_paths/core/clients/ClientManager.py +231 -0
  12. wayfinder_paths/core/clients/HyperlendClient.py +151 -0
  13. wayfinder_paths/core/clients/LedgerClient.py +222 -0
  14. wayfinder_paths/core/clients/PoolClient.py +96 -0
  15. wayfinder_paths/core/clients/SimulationClient.py +180 -0
  16. wayfinder_paths/core/clients/TokenClient.py +73 -0
  17. wayfinder_paths/core/clients/TransactionClient.py +47 -0
  18. wayfinder_paths/core/clients/WalletClient.py +90 -0
  19. wayfinder_paths/core/clients/WayfinderClient.py +258 -0
  20. wayfinder_paths/core/clients/__init__.py +48 -0
  21. wayfinder_paths/core/clients/protocols.py +295 -0
  22. wayfinder_paths/core/clients/sdk_example.py +115 -0
  23. wayfinder_paths/core/config.py +369 -0
  24. wayfinder_paths/core/constants/__init__.py +26 -0
  25. wayfinder_paths/core/constants/base.py +25 -0
  26. wayfinder_paths/core/constants/erc20_abi.py +118 -0
  27. wayfinder_paths/core/constants/hyperlend_abi.py +152 -0
  28. wayfinder_paths/core/engine/VaultJob.py +182 -0
  29. wayfinder_paths/core/engine/__init__.py +5 -0
  30. wayfinder_paths/core/engine/manifest.py +97 -0
  31. wayfinder_paths/core/services/__init__.py +0 -0
  32. wayfinder_paths/core/services/base.py +177 -0
  33. wayfinder_paths/core/services/local_evm_txn.py +429 -0
  34. wayfinder_paths/core/services/local_token_txn.py +231 -0
  35. wayfinder_paths/core/services/web3_service.py +45 -0
  36. wayfinder_paths/core/settings.py +61 -0
  37. wayfinder_paths/core/strategies/Strategy.py +183 -0
  38. wayfinder_paths/core/strategies/__init__.py +5 -0
  39. wayfinder_paths/core/strategies/base.py +7 -0
  40. wayfinder_paths/core/utils/__init__.py +1 -0
  41. wayfinder_paths/core/utils/evm_helpers.py +165 -0
  42. wayfinder_paths/core/utils/wallets.py +77 -0
  43. wayfinder_paths/core/wallets/README.md +91 -0
  44. wayfinder_paths/core/wallets/WalletManager.py +56 -0
  45. wayfinder_paths/core/wallets/__init__.py +7 -0
  46. wayfinder_paths/run_strategy.py +409 -0
  47. wayfinder_paths/scripts/__init__.py +0 -0
  48. wayfinder_paths/scripts/create_strategy.py +181 -0
  49. wayfinder_paths/scripts/make_wallets.py +160 -0
  50. wayfinder_paths/scripts/validate_manifests.py +213 -0
  51. wayfinder_paths/tests/__init__.py +0 -0
  52. wayfinder_paths/tests/test_smoke_manifest.py +48 -0
  53. wayfinder_paths/tests/test_test_coverage.py +212 -0
  54. wayfinder_paths/tests/test_utils.py +64 -0
  55. wayfinder_paths/vaults/__init__.py +0 -0
  56. wayfinder_paths/vaults/adapters/__init__.py +0 -0
  57. wayfinder_paths/vaults/adapters/balance_adapter/README.md +104 -0
  58. wayfinder_paths/vaults/adapters/balance_adapter/adapter.py +257 -0
  59. wayfinder_paths/vaults/adapters/balance_adapter/examples.json +6 -0
  60. wayfinder_paths/vaults/adapters/balance_adapter/manifest.yaml +8 -0
  61. wayfinder_paths/vaults/adapters/balance_adapter/test_adapter.py +83 -0
  62. wayfinder_paths/vaults/adapters/brap_adapter/README.md +249 -0
  63. wayfinder_paths/vaults/adapters/brap_adapter/__init__.py +7 -0
  64. wayfinder_paths/vaults/adapters/brap_adapter/adapter.py +717 -0
  65. wayfinder_paths/vaults/adapters/brap_adapter/examples.json +175 -0
  66. wayfinder_paths/vaults/adapters/brap_adapter/manifest.yaml +11 -0
  67. wayfinder_paths/vaults/adapters/brap_adapter/test_adapter.py +288 -0
  68. wayfinder_paths/vaults/adapters/hyperlend_adapter/__init__.py +7 -0
  69. wayfinder_paths/vaults/adapters/hyperlend_adapter/adapter.py +298 -0
  70. wayfinder_paths/vaults/adapters/hyperlend_adapter/manifest.yaml +10 -0
  71. wayfinder_paths/vaults/adapters/hyperlend_adapter/test_adapter.py +267 -0
  72. wayfinder_paths/vaults/adapters/ledger_adapter/README.md +158 -0
  73. wayfinder_paths/vaults/adapters/ledger_adapter/__init__.py +7 -0
  74. wayfinder_paths/vaults/adapters/ledger_adapter/adapter.py +286 -0
  75. wayfinder_paths/vaults/adapters/ledger_adapter/examples.json +131 -0
  76. wayfinder_paths/vaults/adapters/ledger_adapter/manifest.yaml +11 -0
  77. wayfinder_paths/vaults/adapters/ledger_adapter/test_adapter.py +202 -0
  78. wayfinder_paths/vaults/adapters/pool_adapter/README.md +218 -0
  79. wayfinder_paths/vaults/adapters/pool_adapter/__init__.py +7 -0
  80. wayfinder_paths/vaults/adapters/pool_adapter/adapter.py +289 -0
  81. wayfinder_paths/vaults/adapters/pool_adapter/examples.json +143 -0
  82. wayfinder_paths/vaults/adapters/pool_adapter/manifest.yaml +10 -0
  83. wayfinder_paths/vaults/adapters/pool_adapter/test_adapter.py +222 -0
  84. wayfinder_paths/vaults/adapters/token_adapter/README.md +101 -0
  85. wayfinder_paths/vaults/adapters/token_adapter/__init__.py +3 -0
  86. wayfinder_paths/vaults/adapters/token_adapter/adapter.py +92 -0
  87. wayfinder_paths/vaults/adapters/token_adapter/examples.json +26 -0
  88. wayfinder_paths/vaults/adapters/token_adapter/manifest.yaml +6 -0
  89. wayfinder_paths/vaults/adapters/token_adapter/test_adapter.py +135 -0
  90. wayfinder_paths/vaults/strategies/__init__.py +0 -0
  91. wayfinder_paths/vaults/strategies/config.py +85 -0
  92. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/README.md +99 -0
  93. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/examples.json +16 -0
  94. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/manifest.yaml +7 -0
  95. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/strategy.py +2328 -0
  96. wayfinder_paths/vaults/strategies/hyperlend_stable_yield_strategy/test_strategy.py +319 -0
  97. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/README.md +95 -0
  98. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/examples.json +17 -0
  99. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/manifest.yaml +17 -0
  100. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/strategy.py +1684 -0
  101. wayfinder_paths/vaults/strategies/stablecoin_yield_strategy/test_strategy.py +350 -0
  102. wayfinder_paths/vaults/templates/adapter/README.md +105 -0
  103. wayfinder_paths/vaults/templates/adapter/adapter.py +26 -0
  104. wayfinder_paths/vaults/templates/adapter/examples.json +8 -0
  105. wayfinder_paths/vaults/templates/adapter/manifest.yaml +6 -0
  106. wayfinder_paths/vaults/templates/adapter/test_adapter.py +49 -0
  107. wayfinder_paths/vaults/templates/strategy/README.md +152 -0
  108. wayfinder_paths/vaults/templates/strategy/examples.json +11 -0
  109. wayfinder_paths/vaults/templates/strategy/manifest.yaml +8 -0
  110. wayfinder_paths/vaults/templates/strategy/strategy.py +57 -0
  111. wayfinder_paths/vaults/templates/strategy/test_strategy.py +197 -0
  112. wayfinder_paths-0.1.1.dist-info/LICENSE +21 -0
  113. wayfinder_paths-0.1.1.dist-info/METADATA +727 -0
  114. wayfinder_paths-0.1.1.dist-info/RECORD +115 -0
  115. wayfinder_paths-0.1.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,96 @@
1
+ """
2
+ Pool Client
3
+ Provides read-only access to pool metadata and analytics via public endpoints.
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from wayfinder_paths.core.clients.AuthClient import AuthClient
9
+ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
+ from wayfinder_paths.core.settings import settings
11
+
12
+
13
+ class PoolClient(WayfinderClient):
14
+ """Client for pool-related read operations"""
15
+
16
+ def __init__(self, api_key: str | None = None):
17
+ super().__init__(api_key=api_key)
18
+ self.api_base_url = f"{settings.WAYFINDER_API_URL}"
19
+ self._auth_client: AuthClient | None = AuthClient(api_key=api_key)
20
+
21
+ async def get_pools_by_ids(
22
+ self,
23
+ *,
24
+ pool_ids: str,
25
+ merge_external: bool | None = None,
26
+ ) -> dict[str, Any]:
27
+ """
28
+ Fetch pools by comma-separated pool ids.
29
+
30
+ Example:
31
+ GET /api/v1/public/pools/?pool_ids=a,b&merge_external=false
32
+ """
33
+ url = f"{self.api_base_url}/public/pools/"
34
+ params: dict[str, Any] = {"pool_ids": pool_ids}
35
+ if merge_external is not None:
36
+ params["merge_external"] = "true" if merge_external else "false"
37
+ response = await self._request("GET", url, params=params, headers={})
38
+ response.raise_for_status()
39
+ data = response.json()
40
+ return data.get("data", data)
41
+
42
+ async def get_all_pools(
43
+ self, *, merge_external: bool | None = None
44
+ ) -> dict[str, Any]:
45
+ """
46
+ Fetch all pools.
47
+
48
+ Example:
49
+ GET /api/v1/public/pools/?merge_external=false
50
+ """
51
+ url = f"{self.api_base_url}/public/pools/"
52
+ params: dict[str, Any] = {}
53
+ if merge_external is not None:
54
+ params["merge_external"] = "true" if merge_external else "false"
55
+ response = await self._request("GET", url, params=params, headers={})
56
+ response.raise_for_status()
57
+ data = response.json()
58
+ return data.get("data", data)
59
+
60
+ async def get_combined_pool_reports(self) -> dict[str, Any]:
61
+ """
62
+ Fetch combined pool reports.
63
+
64
+ GET /api/v1/public/pools/combined/
65
+ """
66
+ url = f"{self.api_base_url}/public/pools/combined/"
67
+ response = await self._request("GET", url, headers={})
68
+ response.raise_for_status()
69
+ data = response.json()
70
+ return data.get("data", data)
71
+
72
+ async def get_llama_matches(self) -> dict[str, Any]:
73
+ """
74
+ Fetch Llama matches for pools.
75
+
76
+ GET /api/v1/public/pools/llama/matches/
77
+ """
78
+ url = f"{self.api_base_url}/public/pools/llama/matches/"
79
+ response = await self._request("GET", url, headers={})
80
+ response.raise_for_status()
81
+ data = response.json()
82
+ return data.get("data", data)
83
+
84
+ async def get_llama_reports(self, *, identifiers: str) -> dict[str, Any]:
85
+ """
86
+ Fetch Llama reports using identifiers (token ids, address_network, or pool ids).
87
+
88
+ Example:
89
+ GET /api/v1/public/pools/llama/reports/?identifiers=pool-1,usd-coin
90
+ """
91
+ url = f"{self.api_base_url}/public/pools/llama/reports/"
92
+ params = {"identifiers": identifiers}
93
+ response = await self._request("GET", url, params=params, headers={})
94
+ response.raise_for_status()
95
+ data = response.json()
96
+ return data.get("data", data)
@@ -0,0 +1,180 @@
1
+ """
2
+ Simulation Client
3
+ Handles blockchain transaction simulations via Gorlami/Tenderly
4
+ """
5
+
6
+ import time
7
+ from typing import Any
8
+
9
+ from loguru import logger
10
+
11
+ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
12
+
13
+
14
+ class SimulationClient(WayfinderClient):
15
+ """Client for blockchain transaction simulations"""
16
+
17
+ def __init__(self, api_key: str | None = None):
18
+ super().__init__(api_key=api_key)
19
+
20
+ async def simulate_send(
21
+ self,
22
+ from_address: str,
23
+ to_address: str,
24
+ token_address: str,
25
+ amount: str,
26
+ chain_id: int,
27
+ initial_balances: dict[str, str],
28
+ ) -> dict[str, Any]:
29
+ """
30
+ Simulate sending native ETH or ERC20 tokens.
31
+
32
+ Args:
33
+ from_address: Source wallet address
34
+ to_address: Destination wallet address
35
+ token_address: Token contract address (use 0x0 for native ETH)
36
+ amount: Amount to send
37
+ chain_id: Blockchain chain ID
38
+ initial_balances: Initial token balances for simulation
39
+
40
+ Returns:
41
+ Simulation result data
42
+ """
43
+ logger.info(
44
+ f"Simulating send: {amount} from {from_address} to {to_address} (chain {chain_id})"
45
+ )
46
+ start_time = time.time()
47
+
48
+ url = f"{self.api_base_url}public/simulate/send/"
49
+
50
+ payload = {
51
+ "from_address": from_address,
52
+ "to_address": to_address,
53
+ "token_address": token_address,
54
+ "amount": amount,
55
+ "chain_id": chain_id,
56
+ "initial_balances": initial_balances,
57
+ }
58
+
59
+ try:
60
+ response = await self._request("POST", url, json=payload, headers={})
61
+ response.raise_for_status()
62
+ data = response.json()
63
+ elapsed = time.time() - start_time
64
+ logger.info(f"Simulation request completed successfully in {elapsed:.2f}s")
65
+ return data.get("data", data)
66
+ except Exception as e:
67
+ elapsed = time.time() - start_time
68
+ logger.error(f"Simulation request failed after {elapsed:.2f}s: {e}")
69
+ raise
70
+
71
+ async def simulate_approve(
72
+ self,
73
+ from_address: str,
74
+ to_address: str,
75
+ token_address: str,
76
+ amount: str,
77
+ chain_id: int,
78
+ initial_balances: dict[str, str],
79
+ clear_approval_first: bool = False,
80
+ ) -> dict[str, Any]:
81
+ """
82
+ Simulate ERC20 token approval.
83
+
84
+ Args:
85
+ from_address: Address approving the tokens
86
+ to_address: Address being approved to spend tokens
87
+ token_address: ERC20 token contract address
88
+ amount: Amount to approve
89
+ chain_id: Blockchain chain ID
90
+ initial_balances: Initial token balances for simulation
91
+ clear_approval_first: Whether to clear existing approval before setting new one
92
+
93
+ Returns:
94
+ Simulation result data
95
+ """
96
+ logger.info(
97
+ f"Simulating approval: {amount} from {from_address} to {to_address} (chain {chain_id})"
98
+ )
99
+ start_time = time.time()
100
+
101
+ url = f"{self.api_base_url}public/simulate/approve/"
102
+
103
+ payload = {
104
+ "from_address": from_address,
105
+ "to_address": to_address,
106
+ "token_address": token_address,
107
+ "amount": amount,
108
+ "chain_id": chain_id,
109
+ "initial_balances": initial_balances,
110
+ "clear_approval_first": clear_approval_first,
111
+ }
112
+
113
+ try:
114
+ response = await self._request("POST", url, json=payload, headers={})
115
+ response.raise_for_status()
116
+ data = response.json()
117
+ elapsed = time.time() - start_time
118
+ logger.info(f"Simulation request completed successfully in {elapsed:.2f}s")
119
+ return data.get("data", data)
120
+ except Exception as e:
121
+ elapsed = time.time() - start_time
122
+ logger.error(f"Simulation request failed after {elapsed:.2f}s: {e}")
123
+ raise
124
+
125
+ async def simulate_swap(
126
+ self,
127
+ from_token_address: str,
128
+ to_token_address: str,
129
+ from_chain_id: int,
130
+ to_chain_id: int,
131
+ amount: str,
132
+ from_address: str,
133
+ slippage: float,
134
+ initial_balances: dict[str, str],
135
+ ) -> dict[str, Any]:
136
+ """
137
+ Simulate token swap operation.
138
+
139
+ Args:
140
+ from_token_address: Source token contract address
141
+ to_token_address: Destination token contract address
142
+ from_chain_id: Source chain ID
143
+ to_chain_id: Destination chain ID
144
+ amount: Amount to swap
145
+ from_address: Wallet address initiating swap
146
+ slippage: Maximum slippage tolerance
147
+ initial_balances: Initial token balances for simulation
148
+
149
+ Returns:
150
+ Simulation result data
151
+ """
152
+ logger.info(
153
+ f"Simulating swap: {from_token_address} -> {to_token_address} (chain {from_chain_id} -> {to_chain_id})"
154
+ )
155
+ start_time = time.time()
156
+
157
+ url = f"{self.api_base_url}public/simulate/swap/"
158
+
159
+ payload = {
160
+ "from_token_address": from_token_address,
161
+ "to_token_address": to_token_address,
162
+ "from_chain_id": from_chain_id,
163
+ "to_chain_id": to_chain_id,
164
+ "amount": amount,
165
+ "from_address": from_address,
166
+ "slippage": slippage,
167
+ "initial_balances": initial_balances,
168
+ }
169
+
170
+ try:
171
+ response = await self._request("POST", url, json=payload, headers={})
172
+ response.raise_for_status()
173
+ data = response.json()
174
+ elapsed = time.time() - start_time
175
+ logger.info(f"Simulation request completed successfully in {elapsed:.2f}s")
176
+ return data.get("data", data)
177
+ except Exception as e:
178
+ elapsed = time.time() - start_time
179
+ logger.error(f"Simulation request failed after {elapsed:.2f}s: {e}")
180
+ raise
@@ -0,0 +1,73 @@
1
+ """
2
+ Token Adapter
3
+ Handles token information, prices, and parsing
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from wayfinder_paths.core.clients.AuthClient import AuthClient
9
+ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
+ from wayfinder_paths.core.settings import settings
11
+
12
+
13
+ class TokenClient(WayfinderClient):
14
+ """Adapter for token-related operations"""
15
+
16
+ def __init__(self, api_key: str | None = None):
17
+ super().__init__(api_key=api_key)
18
+ self.api_base_url = f"{self.api_base_url}/tokens"
19
+ self._auth_client: AuthClient | None = AuthClient(api_key=api_key)
20
+
21
+ # ============== Public (No-Auth) Endpoints ==============
22
+
23
+ async def get_token_details(
24
+ self, token_id: str, force_refresh: bool = False
25
+ ) -> dict[str, Any]:
26
+ """
27
+ Get token data including price from the token-details endpoint
28
+
29
+ Args:
30
+ token_id: Token identifier or address
31
+
32
+ Returns:
33
+ Full token data including price information
34
+ """
35
+ url = f"{settings.WAYFINDER_API_URL}/public/tokens/detail/"
36
+ params = {
37
+ "query": token_id,
38
+ "get_chart": "false",
39
+ "force_refresh": str(force_refresh),
40
+ }
41
+ # Public endpoint: do not pass auth headers
42
+ response = await self._request("GET", url, params=params, headers={})
43
+ response.raise_for_status()
44
+ data = response.json()
45
+ return data.get("data", data)
46
+
47
+ async def get_gas_token(self, chain_code: str) -> dict[str, Any]:
48
+ """
49
+ Fetch the native gas token for a given chain code via public endpoint.
50
+ Example: GET /api/v1/public/tokens/gas/?chain_code=base
51
+ """
52
+ url = f"{settings.WAYFINDER_API_URL}/public/tokens/gas/"
53
+ params = {"chain_code": chain_code}
54
+ # Public endpoint: do not pass auth headers
55
+ response = await self._request("GET", url, params=params, headers={})
56
+ response.raise_for_status()
57
+ data = response.json()
58
+ 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)
@@ -0,0 +1,47 @@
1
+ """
2
+ Transaction Client
3
+ Handles transaction building, gas estimation, and monitoring
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from wayfinder_paths.core.clients.AuthClient import AuthClient
9
+ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
+ from wayfinder_paths.core.settings import settings
11
+
12
+
13
+ class TransactionClient(WayfinderClient):
14
+ """Client for transaction operations"""
15
+
16
+ def __init__(self, api_key: str | None = None):
17
+ super().__init__(api_key=api_key)
18
+ self.api_base_url = f"{self.api_base_url}/transactions"
19
+ self._auth_client: AuthClient | None = AuthClient(api_key=api_key)
20
+
21
+ # ============== Protected (Auth Required) Endpoints ==============
22
+
23
+ async def build_send(
24
+ self,
25
+ from_address: str,
26
+ to_address: str,
27
+ token_address: str,
28
+ amount: float,
29
+ chain_id: int,
30
+ ) -> dict[str, Any]:
31
+ """
32
+ Build a send transaction payload for EVM tokens/native transfers.
33
+
34
+ GET /api/v1/public/transactions/build-send/?from_address=...&to_address=...&token_address=...&amount=...&chain_id=...
35
+ """
36
+ url = f"{settings.WAYFINDER_API_URL}/public/transactions/build-send/"
37
+ params = {
38
+ "from_address": from_address,
39
+ "to_address": to_address,
40
+ "token_address": token_address,
41
+ "amount": str(amount),
42
+ "chain_id": str(chain_id),
43
+ }
44
+ response = await self._authed_request("GET", url, params=params)
45
+ response.raise_for_status()
46
+ data = response.json()
47
+ return data.get("data", data)
@@ -0,0 +1,90 @@
1
+ """
2
+ Wallet Client
3
+ Fetches wallet-related data such as aggregated balances for the authenticated user.
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from wayfinder_paths.core.clients.AuthClient import AuthClient
9
+ from wayfinder_paths.core.clients.WayfinderClient import WayfinderClient
10
+ from wayfinder_paths.core.settings import settings
11
+
12
+
13
+ class WalletClient(WayfinderClient):
14
+ def __init__(self, api_key: str | None = None):
15
+ super().__init__(api_key=api_key)
16
+ self.api_base_url = f"{settings.WAYFINDER_API_URL}"
17
+ self._auth_client = AuthClient(api_key=api_key)
18
+
19
+ async def get_token_balance_for_wallet(
20
+ self,
21
+ *,
22
+ token_id: str,
23
+ wallet_address: str,
24
+ human_readable: bool = True,
25
+ ) -> dict[str, Any]:
26
+ """
27
+ Fetch a single token balance for an explicit wallet address.
28
+
29
+ Mirrors POST /api/v1/public/balances/token/
30
+ """
31
+ url = f"{self.api_base_url}/public/balances/token/"
32
+ payload = {
33
+ "token_id": token_id,
34
+ "wallet_address": wallet_address,
35
+ "human_readable": human_readable,
36
+ }
37
+ response = await self._authed_request("POST", url, json=payload)
38
+ data = response.json()
39
+ return data.get("data", data)
40
+
41
+ async def get_pool_balance_for_wallet(
42
+ self,
43
+ *,
44
+ pool_address: str,
45
+ chain_id: int,
46
+ user_address: str,
47
+ human_readable: bool = True,
48
+ ) -> dict[str, Any]:
49
+ """
50
+ Fetch a wallet's LP/share balance for a given pool address and chain.
51
+
52
+ Mirrors POST /api/v1/public/balances/pool/
53
+ """
54
+ url = f"{self.api_base_url}/public/balances/pool/"
55
+ payload = {
56
+ "pool_address": pool_address,
57
+ "chain_id": chain_id,
58
+ "user_address": user_address,
59
+ "human_readable": human_readable,
60
+ }
61
+ response = await self._authed_request("POST", url, json=payload)
62
+ data = response.json()
63
+ return data.get("data", data)
64
+
65
+ async def get_all_enriched_token_balances_for_wallet(
66
+ self,
67
+ *,
68
+ wallet_address: str,
69
+ enrich: bool = True,
70
+ from_cache: bool = False,
71
+ add_llama: bool = True,
72
+ ) -> dict[str, Any]:
73
+ """
74
+ Fetch all token balances for a wallet with enrichment via the enriched endpoint.
75
+
76
+ Mirrors POST /api/v1/public/balances/enriched/
77
+ """
78
+ url = f"{self.api_base_url}/public/balances/enriched/"
79
+ payload = {
80
+ "wallet_address": wallet_address,
81
+ "enrich": enrich,
82
+ "from_cache": from_cache,
83
+ "add_llama": add_llama,
84
+ }
85
+ try:
86
+ response = await self._authed_request("POST", url, json=payload)
87
+ data = response.json()
88
+ return data.get("data", data)
89
+ except Exception:
90
+ raise