tonutils 2.0.1b2__py3-none-any.whl → 2.0.1b3__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.
Files changed (76) hide show
  1. tonutils/__init__.py +0 -2
  2. tonutils/__meta__.py +1 -1
  3. tonutils/clients/__init__.py +5 -9
  4. tonutils/clients/adnl/__init__.py +5 -1
  5. tonutils/clients/adnl/balancer.py +319 -125
  6. tonutils/clients/adnl/client.py +187 -51
  7. tonutils/clients/adnl/provider/config.py +19 -25
  8. tonutils/clients/adnl/provider/models.py +4 -0
  9. tonutils/clients/adnl/provider/provider.py +191 -145
  10. tonutils/clients/adnl/provider/transport.py +38 -32
  11. tonutils/clients/adnl/provider/workers/base.py +0 -2
  12. tonutils/clients/adnl/provider/workers/pinger.py +1 -1
  13. tonutils/clients/adnl/provider/workers/reader.py +3 -2
  14. tonutils/clients/adnl/{provider/builder.py → utils.py} +62 -2
  15. tonutils/clients/http/__init__.py +11 -8
  16. tonutils/clients/http/balancer.py +75 -63
  17. tonutils/clients/http/clients/__init__.py +13 -0
  18. tonutils/clients/http/clients/chainstack.py +48 -0
  19. tonutils/clients/http/clients/quicknode.py +47 -0
  20. tonutils/clients/http/clients/tatum.py +56 -0
  21. tonutils/clients/http/{tonapi/client.py → clients/tonapi.py} +31 -31
  22. tonutils/clients/http/{toncenter/client.py → clients/toncenter.py} +59 -48
  23. tonutils/clients/http/providers/__init__.py +4 -0
  24. tonutils/clients/http/providers/base.py +201 -0
  25. tonutils/clients/http/providers/response.py +85 -0
  26. tonutils/clients/http/providers/tonapi/__init__.py +3 -0
  27. tonutils/clients/http/{tonapi → providers/tonapi}/models.py +1 -0
  28. tonutils/clients/http/providers/tonapi/provider.py +125 -0
  29. tonutils/clients/http/providers/toncenter/__init__.py +3 -0
  30. tonutils/clients/http/{toncenter → providers/toncenter}/models.py +1 -0
  31. tonutils/clients/http/providers/toncenter/provider.py +119 -0
  32. tonutils/clients/http/utils.py +140 -0
  33. tonutils/clients/limiter.py +115 -0
  34. tonutils/contracts/__init__.py +4 -0
  35. tonutils/contracts/base.py +33 -20
  36. tonutils/contracts/dns/methods.py +2 -2
  37. tonutils/contracts/jetton/methods.py +2 -2
  38. tonutils/contracts/nft/methods.py +2 -2
  39. tonutils/contracts/nft/tlb.py +1 -1
  40. tonutils/{protocols/contract.py → contracts/protocol.py} +29 -29
  41. tonutils/contracts/telegram/methods.py +2 -2
  42. tonutils/contracts/vanity/vanity.py +1 -1
  43. tonutils/contracts/wallet/__init__.py +2 -0
  44. tonutils/contracts/wallet/base.py +3 -3
  45. tonutils/contracts/wallet/messages.py +1 -1
  46. tonutils/contracts/wallet/methods.py +2 -2
  47. tonutils/{protocols/wallet.py → contracts/wallet/protocol.py} +35 -35
  48. tonutils/contracts/wallet/versions/v5.py +3 -3
  49. tonutils/exceptions.py +134 -226
  50. tonutils/types.py +115 -0
  51. tonutils/utils.py +3 -3
  52. {tonutils-2.0.1b2.dist-info → tonutils-2.0.1b3.dist-info}/METADATA +2 -2
  53. tonutils-2.0.1b3.dist-info/RECORD +93 -0
  54. tonutils/clients/adnl/provider/limiter.py +0 -56
  55. tonutils/clients/adnl/stack.py +0 -64
  56. tonutils/clients/http/chainstack/__init__.py +0 -4
  57. tonutils/clients/http/chainstack/client.py +0 -63
  58. tonutils/clients/http/chainstack/provider.py +0 -44
  59. tonutils/clients/http/quicknode/__init__.py +0 -4
  60. tonutils/clients/http/quicknode/client.py +0 -60
  61. tonutils/clients/http/quicknode/provider.py +0 -42
  62. tonutils/clients/http/tatum/__init__.py +0 -4
  63. tonutils/clients/http/tatum/client.py +0 -66
  64. tonutils/clients/http/tatum/provider.py +0 -53
  65. tonutils/clients/http/tonapi/__init__.py +0 -4
  66. tonutils/clients/http/tonapi/provider.py +0 -150
  67. tonutils/clients/http/tonapi/stack.py +0 -71
  68. tonutils/clients/http/toncenter/__init__.py +0 -4
  69. tonutils/clients/http/toncenter/provider.py +0 -145
  70. tonutils/clients/http/toncenter/stack.py +0 -73
  71. tonutils/protocols/__init__.py +0 -9
  72. tonutils-2.0.1b2.dist-info/RECORD +0 -98
  73. /tonutils/{protocols/client.py → clients/protocol.py} +0 -0
  74. {tonutils-2.0.1b2.dist-info → tonutils-2.0.1b3.dist-info}/WHEEL +0 -0
  75. {tonutils-2.0.1b2.dist-info → tonutils-2.0.1b3.dist-info}/licenses/LICENSE +0 -0
  76. {tonutils-2.0.1b2.dist-info → tonutils-2.0.1b3.dist-info}/top_level.txt +0 -0
@@ -11,10 +11,12 @@ from pytoniq_core import (
11
11
  SimpleAccount,
12
12
  begin_cell,
13
13
  check_account_proof,
14
+ Slice,
15
+ VmTuple,
14
16
  )
15
17
 
16
- from tonutils.types import ContractState, ContractStateInfo
17
- from tonutils.utils import cell_to_hex
18
+ from tonutils.types import ContractState, ContractStateInfo, StackItems, StackItem
19
+ from tonutils.utils import cell_to_hex, norm_stack_num, norm_stack_cell
18
20
 
19
21
 
20
22
  def build_config_all(config_proof: Cell) -> t.Dict[int, t.Any]:
@@ -109,3 +111,61 @@ def build_contract_state_info(
109
111
  info.state = ContractState.NONEXIST
110
112
 
111
113
  return info
114
+
115
+
116
+ def decode_stack(items: t.List[t.Any]) -> StackItems:
117
+ """
118
+ Decode VM stack items into internal Python structures.
119
+
120
+ Supports:
121
+ - int → int
122
+ - Cell/Slice → normalized cell
123
+ - Address → address cell
124
+ - VmTuple/list → recursive decode
125
+ - None → None
126
+
127
+ :param items: Raw VM stack items
128
+ :return: Normalized Python stack values
129
+ """
130
+
131
+ out: StackItems = []
132
+ for item in items:
133
+ if item is None:
134
+ out.append(None)
135
+ elif isinstance(item, int):
136
+ out.append(norm_stack_num(item))
137
+ elif isinstance(item, Address):
138
+ out.append(item.to_cell())
139
+ elif isinstance(item, (Cell, Slice)):
140
+ out.append(norm_stack_cell(item))
141
+ elif isinstance(item, VmTuple):
142
+ out.append(decode_stack(item.list))
143
+ elif isinstance(item, list):
144
+ out.append(decode_stack(item))
145
+ return out
146
+
147
+
148
+ def encode_stack(items: t.List[StackItem]) -> t.List[t.Any]:
149
+ """
150
+ Encode Python stack values into VM-compatible format.
151
+
152
+ Supports:
153
+ - int → int
154
+ - Cell/Slice → cell/slice
155
+ - Address → cell slice
156
+ - list/tuple → recursive encode
157
+
158
+ :param items: Normalized Python stack items
159
+ :return: VM-encoded stack values
160
+ """
161
+ out: t.List[t.Any] = []
162
+ for item in items:
163
+ if isinstance(item, int):
164
+ out.append(item)
165
+ elif isinstance(item, Address):
166
+ out.append(item.to_cell().to_slice())
167
+ elif isinstance(item, (Cell, Slice)):
168
+ out.append(item)
169
+ elif isinstance(item, (list, tuple)):
170
+ out.append(encode_stack(list(item)))
171
+ return out
@@ -1,18 +1,21 @@
1
1
  from .balancer import HttpBalancer
2
- from .chainstack import ChainstackHttpClient, ChainstackHttpProvider
3
- from .quicknode import QuicknodeHttpClient, QuicknodeHttpProvider
4
- from .tatum import TatumHttpClient, TatumHttpProvider
5
- from .tonapi import TonapiHttpClient, TonapiHttpProvider
6
- from .toncenter import ToncenterHttpClient, ToncenterHttpProvider
2
+ from .clients import (
3
+ ChainstackHttpClient,
4
+ QuicknodeHttpClient,
5
+ TatumHttpClient,
6
+ TonapiHttpClient,
7
+ ToncenterHttpClient,
8
+ )
9
+ from .providers import (
10
+ TonapiHttpProvider,
11
+ ToncenterHttpProvider,
12
+ )
7
13
 
8
14
  __all__ = [
9
15
  "HttpBalancer",
10
16
  "ChainstackHttpClient",
11
- "ChainstackHttpProvider",
12
17
  "QuicknodeHttpClient",
13
- "QuicknodeHttpProvider",
14
18
  "TatumHttpClient",
15
- "TatumHttpProvider",
16
19
  "TonapiHttpClient",
17
20
  "TonapiHttpProvider",
18
21
  "ToncenterHttpClient",
@@ -7,18 +7,19 @@ from contextlib import suppress
7
7
  from dataclasses import dataclass
8
8
  from itertools import cycle
9
9
 
10
- from pyapiq.exceptions import (
11
- APIQException,
12
- APIClientResponseError,
13
- APIClientServerError,
14
- APIClientTooManyRequestsError,
15
- RateLimitExceeded,
16
- )
17
10
  from pytoniq_core import Transaction
18
11
 
19
12
  from tonutils.clients.base import BaseClient
20
- from tonutils.clients.http.quicknode import QuicknodeHttpClient
21
- from tonutils.exceptions import ClientError, ClientNotConnectedError
13
+ from tonutils.clients.http.clients.quicknode import QuicknodeHttpClient
14
+ from tonutils.exceptions import (
15
+ BalancerError,
16
+ ClientError,
17
+ TransportError,
18
+ ProviderError,
19
+ ProviderResponseError,
20
+ RunGetMethodError,
21
+ ProviderTimeoutError,
22
+ )
22
23
  from tonutils.types import ClientType, ContractStateInfo, NetworkGlobalID
23
24
 
24
25
  _T = t.TypeVar("_T")
@@ -52,6 +53,7 @@ class HttpBalancer(BaseClient):
52
53
  *,
53
54
  network: NetworkGlobalID = NetworkGlobalID.MAINNET,
54
55
  clients: t.List[BaseClient],
56
+ request_timeout: float = 12.0,
55
57
  ) -> None:
56
58
  """
57
59
  Initialize HTTP balancer.
@@ -71,6 +73,8 @@ class HttpBalancer(BaseClient):
71
73
 
72
74
  :param network: Target TON network (mainnet or testnet)
73
75
  :param clients: List of HTTP BaseClient instances to balance between
76
+ :param request_timeout: Maximum total time in seconds for a balancer operation,
77
+ including all failover attempts across providers
74
78
  """
75
79
  self.network = network
76
80
 
@@ -83,6 +87,8 @@ class HttpBalancer(BaseClient):
83
87
  self._retry_after_base = 1.0
84
88
  self._retry_after_max = 10.0
85
89
 
90
+ self._request_timeout = request_timeout
91
+
86
92
  def __init_clients(
87
93
  self,
88
94
  clients: t.List[BaseClient],
@@ -109,6 +115,25 @@ class HttpBalancer(BaseClient):
109
115
  self._clients.append(client)
110
116
  self._states.append(state)
111
117
 
118
+ @property
119
+ def provider(self) -> t.Any:
120
+ """
121
+ Provider of the currently selected client.
122
+
123
+ :return: Provider instance of chosen HTTP client
124
+ """
125
+ c = self._pick_client()
126
+ return c.provider
127
+
128
+ @property
129
+ def is_connected(self) -> bool:
130
+ """
131
+ Check whether at least one underlying client is connected.
132
+
133
+ :return: True if any client is connected, otherwise False
134
+ """
135
+ return any(c.is_connected for c in self._clients)
136
+
112
137
  @property
113
138
  def clients(self) -> t.Tuple[BaseClient, ...]:
114
139
  """
@@ -146,27 +171,6 @@ class HttpBalancer(BaseClient):
146
171
  if state.retry_after is not None and state.retry_after > now
147
172
  )
148
173
 
149
- @property
150
- def provider(self) -> t.Any:
151
- """
152
- Provider of the currently selected client.
153
-
154
- :return: Provider instance of chosen HTTP client
155
- """
156
- if not self.is_connected:
157
- raise ClientNotConnectedError(self)
158
- client = self._pick_client()
159
- return client.provider
160
-
161
- @property
162
- def is_connected(self) -> bool:
163
- """
164
- Check whether at least one underlying client is connected.
165
-
166
- :return: True if any client is connected, otherwise False
167
- """
168
- return any(c.is_connected for c in self._clients)
169
-
170
174
  async def __aenter__(self) -> HttpBalancer:
171
175
  """
172
176
  Enter async context manager and connect all underlying clients.
@@ -219,7 +223,7 @@ class HttpBalancer(BaseClient):
219
223
  height_candidates.append((wait, state.error_count, state))
220
224
 
221
225
  if not height_candidates:
222
- raise ClientError("No available HTTP clients in HttpBalancer.")
226
+ raise BalancerError("no available HTTP clients")
223
227
 
224
228
  height_candidates.sort(key=lambda x: (x[0], x[1]))
225
229
  best_wait, best_err, _ = height_candidates[0]
@@ -292,39 +296,47 @@ class HttpBalancer(BaseClient):
292
296
  :param func: Callable performing an operation using a client
293
297
  :return: Result of the successful invocation
294
298
  """
295
- last_exc: t.Optional[BaseException] = None
296
-
297
- for _ in range(len(self._clients)):
298
- if not self.alive_clients:
299
- break
300
-
301
- client = self._pick_client()
302
-
303
- try:
304
- result = await func(client)
305
- except (APIClientTooManyRequestsError, RateLimitExceeded) as e:
306
- self._mark_error(client, is_rate_limit=True)
307
- last_exc = e
308
- continue
309
- except APIClientResponseError as e:
310
- last_exc = e
311
- break
312
- except (APIClientServerError, APIQException) as e:
313
- self._mark_error(client, is_rate_limit=False)
314
- last_exc = e
315
- continue
316
- except Exception as e:
317
- self._mark_error(client, is_rate_limit=False)
318
- last_exc = e
319
- continue
320
-
321
- self._mark_success(client)
322
- return result
323
-
324
- if last_exc is not None:
325
- raise last_exc
326
299
 
327
- raise ClientError("All HTTP clients failed to process request.")
300
+ async def _run() -> _T:
301
+ last_exc: t.Optional[BaseException] = None
302
+
303
+ for _ in range(len(self._clients)):
304
+ if not self.alive_clients:
305
+ break
306
+
307
+ client = self._pick_client()
308
+
309
+ try:
310
+ result = await func(client)
311
+
312
+ except RunGetMethodError:
313
+ raise
314
+ except ProviderResponseError as e:
315
+ is_rate_limit = e.code == 429
316
+ self._mark_error(client, is_rate_limit=is_rate_limit)
317
+ last_exc = e
318
+ continue
319
+ except (TransportError, ProviderError) as e:
320
+ self._mark_error(client, is_rate_limit=False)
321
+ last_exc = e
322
+ continue
323
+
324
+ self._mark_success(client)
325
+ return result
326
+
327
+ if last_exc is not None:
328
+ raise last_exc
329
+
330
+ raise ClientError("all HTTP clients failed to process request.")
331
+
332
+ try:
333
+ return await asyncio.wait_for(_run(), timeout=self._request_timeout)
334
+ except asyncio.TimeoutError as exc:
335
+ raise ProviderTimeoutError(
336
+ timeout=self._request_timeout,
337
+ endpoint="http balancer",
338
+ operation="failover request",
339
+ ) from exc
328
340
 
329
341
  async def _send_boc(self, boc: str) -> None:
330
342
  async def _call(client: BaseClient) -> None:
@@ -0,0 +1,13 @@
1
+ from .chainstack import ChainstackHttpClient
2
+ from .quicknode import QuicknodeHttpClient
3
+ from .tatum import TatumHttpClient
4
+ from .tonapi import TonapiHttpClient
5
+ from .toncenter import ToncenterHttpClient
6
+
7
+ __all__ = [
8
+ "ChainstackHttpClient",
9
+ "QuicknodeHttpClient",
10
+ "TatumHttpClient",
11
+ "TonapiHttpClient",
12
+ "ToncenterHttpClient",
13
+ ]
@@ -0,0 +1,48 @@
1
+ import typing as t
2
+
3
+ from aiohttp import ClientSession
4
+
5
+ from tonutils.clients.http.clients.toncenter import ToncenterHttpClient
6
+ from tonutils.types import NetworkGlobalID, RetryPolicy
7
+
8
+
9
+ class ChainstackHttpClient(ToncenterHttpClient):
10
+
11
+ def __init__(
12
+ self,
13
+ *,
14
+ network: NetworkGlobalID,
15
+ http_provider_url: str,
16
+ timeout: float = 10.0,
17
+ session: t.Optional[ClientSession] = None,
18
+ headers: t.Optional[t.Dict[str, str]] = None,
19
+ cookies: t.Optional[t.Dict[str, str]] = None,
20
+ rps_limit: t.Optional[int] = None,
21
+ rps_period: float = 1.0,
22
+ retry_policy: t.Optional[RetryPolicy] = None,
23
+ ) -> None:
24
+ """
25
+ Initialize Chainstack HTTP client.
26
+
27
+ :param network: Target TON network (mainnet or testnet)
28
+ :param http_provider_url: Chainstack TON HTTP endpoint URL
29
+ You can obtain a personal endpoint on the Chainstack website: https://chainstack.com/
30
+ :param timeout: Total request timeout in seconds.
31
+ :param session: Optional external aiohttp session.
32
+ :param headers: Default headers for owned session.
33
+ :param cookies: Default cookies for owned session.
34
+ :param rps_limit: Optional requests-per-period limit.
35
+ :param rps_period: Rate limit period in seconds.
36
+ :param retry_policy: Optional retry policy that defines per-error-code retry rules
37
+ """
38
+ super().__init__(
39
+ network=network,
40
+ base_url=http_provider_url,
41
+ timeout=timeout,
42
+ session=session,
43
+ headers=headers,
44
+ cookies=cookies,
45
+ rps_limit=rps_limit,
46
+ rps_period=rps_period,
47
+ retry_policy=retry_policy,
48
+ )
@@ -0,0 +1,47 @@
1
+ import typing as t
2
+
3
+ from aiohttp import ClientSession
4
+
5
+ from tonutils.clients.http.clients.toncenter import ToncenterHttpClient
6
+ from tonutils.types import NetworkGlobalID, RetryPolicy
7
+
8
+
9
+ class QuicknodeHttpClient(ToncenterHttpClient):
10
+
11
+ def __init__(
12
+ self,
13
+ *,
14
+ http_provider_url: str,
15
+ timeout: float = 10.0,
16
+ session: t.Optional[ClientSession] = None,
17
+ headers: t.Optional[t.Dict[str, str]] = None,
18
+ cookies: t.Optional[t.Dict[str, str]] = None,
19
+ rps_limit: t.Optional[int] = None,
20
+ rps_period: float = 1.0,
21
+ retry_policy: t.Optional[RetryPolicy] = None,
22
+ ) -> None:
23
+ """
24
+ Initialize QuickNode HTTP client.
25
+
26
+ :param http_provider_url: QuickNode TON HTTP endpoint URL.
27
+ You can obtain a personal endpoint on the QuickNode website: https://www.quicknode.com/
28
+ :param timeout: Total request timeout in seconds.
29
+ :param session: Optional external aiohttp session.
30
+ :param headers: Default headers for owned session.
31
+ :param cookies: Default cookies for owned session.
32
+ :param rps_limit: Optional requests-per-period limit.
33
+ :param rps_period: Rate limit period in seconds.
34
+ :param retry_policy: Optional retry policy that defines per-error-code retry rules
35
+ """
36
+
37
+ super().__init__(
38
+ network=NetworkGlobalID.MAINNET,
39
+ base_url=http_provider_url,
40
+ timeout=timeout,
41
+ session=session,
42
+ headers=headers,
43
+ cookies=cookies,
44
+ rps_limit=rps_limit,
45
+ rps_period=rps_period,
46
+ retry_policy=retry_policy,
47
+ )
@@ -0,0 +1,56 @@
1
+ import typing as t
2
+
3
+ from aiohttp import ClientSession
4
+
5
+ from tonutils.clients.http.clients.toncenter import ToncenterHttpClient
6
+ from tonutils.types import NetworkGlobalID, RetryPolicy
7
+
8
+
9
+ class TatumHttpClient(ToncenterHttpClient):
10
+
11
+ def __init__(
12
+ self,
13
+ *,
14
+ network: NetworkGlobalID,
15
+ api_key: str,
16
+ base_url: t.Optional[str] = None,
17
+ timeout: float = 10.0,
18
+ session: t.Optional[ClientSession] = None,
19
+ headers: t.Optional[t.Dict[str, str]] = None,
20
+ cookies: t.Optional[t.Dict[str, str]] = None,
21
+ rps_limit: t.Optional[int] = None,
22
+ rps_period: float = 1.0,
23
+ retry_policy: t.Optional[RetryPolicy] = None,
24
+ ) -> None:
25
+ """
26
+ Initialize Tatum HTTP client.
27
+
28
+ :param network: Target TON network (mainnet or testnet)
29
+ :param api_key: Tatum API key
30
+ You can get an API key on the Tatum website: https://tatum.io/
31
+ :param base_url: Optional custom Tatum base URL
32
+ :param timeout: Total request timeout in seconds.
33
+ :param session: Optional external aiohttp session.
34
+ :param headers: Default headers for owned session.
35
+ :param cookies: Default cookies for owned session.
36
+ :param rps_limit: Optional requests-per-period limit.
37
+ :param rps_period: Rate limit period in seconds.
38
+ :param retry_policy: Optional retry policy that defines per-error-code retry rules
39
+ """
40
+ urls = {
41
+ NetworkGlobalID.MAINNET: "https://ton-mainnet.gateway.tatum.io",
42
+ NetworkGlobalID.TESTNET: "https://ton-testnet.gateway.tatum.io",
43
+ }
44
+ base_url = base_url or urls.get(network)
45
+ super().__init__(
46
+ network=network,
47
+ api_key=api_key,
48
+ base_url=base_url,
49
+ timeout=timeout,
50
+ session=session,
51
+ headers=headers,
52
+ cookies=cookies,
53
+ rps_limit=rps_limit,
54
+ rps_period=rps_period,
55
+ retry_policy=retry_policy,
56
+ )
@@ -6,15 +6,16 @@ from aiohttp import ClientSession
6
6
  from pytoniq_core import Cell, Slice, Transaction
7
7
 
8
8
  from tonutils.clients.base import BaseClient
9
- from tonutils.clients.http.tonapi.models import BlockchainMessagePayload
10
- from tonutils.clients.http.tonapi.provider import TonapiHttpProvider
11
- from tonutils.clients.http.tonapi.stack import decode_stack, encode_stack
12
- from tonutils.exceptions import ClientError, ClientNotConnectedError
9
+ from tonutils.clients.http.providers.tonapi.models import BlockchainMessagePayload
10
+ from tonutils.clients.http.providers.tonapi.provider import TonapiHttpProvider
11
+ from tonutils.clients.http.utils import encode_tonapi_stack, decode_tonapi_stack
12
+ from tonutils.exceptions import ClientError, RunGetMethodError
13
13
  from tonutils.types import (
14
14
  ClientType,
15
15
  ContractState,
16
16
  ContractStateInfo,
17
17
  NetworkGlobalID,
18
+ RetryPolicy,
18
19
  )
19
20
  from tonutils.utils import (
20
21
  cell_to_hex,
@@ -33,11 +34,13 @@ class TonapiHttpClient(BaseClient):
33
34
  network: NetworkGlobalID = NetworkGlobalID.MAINNET,
34
35
  api_key: str,
35
36
  base_url: t.Optional[str] = None,
36
- timeout: int = 10,
37
+ timeout: float = 10.0,
37
38
  session: t.Optional[ClientSession] = None,
39
+ headers: t.Optional[t.Dict[str, str]] = None,
40
+ cookies: t.Optional[t.Dict[str, str]] = None,
38
41
  rps_limit: t.Optional[int] = None,
39
42
  rps_period: float = 1.0,
40
- rps_retries: int = 2,
43
+ retry_policy: t.Optional[RetryPolicy] = None,
41
44
  ) -> None:
42
45
  """
43
46
  Initialize Tonapi HTTP client.
@@ -46,11 +49,13 @@ class TonapiHttpClient(BaseClient):
46
49
  :param api_key: Tonapi API key
47
50
  You can get an API key on the Tonconsole website: https://tonconsole.com/
48
51
  :param base_url: Optional custom Tonapi base URL
49
- :param timeout: HTTP request timeout in seconds
50
- :param session: Optional externally managed aiohttp.ClientSession
51
- :param rps_limit: Optional requests-per-second limit
52
- :param rps_period: Time window in seconds for rate limiting
53
- :param rps_retries: Number of retries on rate limiting
52
+ :param timeout: Total request timeout in seconds.
53
+ :param session: Optional external aiohttp session.
54
+ :param headers: Default headers for owned session.
55
+ :param cookies: Default cookies for owned session.
56
+ :param rps_limit: Optional requests-per-period limit.
57
+ :param rps_period: Rate limit period in seconds.
58
+ :param retry_policy: Optional retry policy that defines per-error-code retry rules
54
59
  """
55
60
  self.network: NetworkGlobalID = network
56
61
  self._provider: TonapiHttpProvider = TonapiHttpProvider(
@@ -59,34 +64,24 @@ class TonapiHttpClient(BaseClient):
59
64
  base_url=base_url,
60
65
  timeout=timeout,
61
66
  session=session,
67
+ headers=headers,
68
+ cookies=cookies,
62
69
  rps_limit=rps_limit,
63
70
  rps_period=rps_period,
64
- rps_retries=rps_retries,
71
+ retry_policy=retry_policy,
65
72
  )
66
73
 
67
74
  @property
68
75
  def provider(self) -> TonapiHttpProvider:
69
- """
70
- Underlying Tonapi HTTP provider.
71
-
72
- :return: TonapiHttpProvider instance used for HTTP requests
73
- """
74
- if not self.is_connected:
75
- raise ClientNotConnectedError(self)
76
76
  return self._provider
77
77
 
78
78
  @property
79
79
  def is_connected(self) -> bool:
80
- """
81
- Check whether HTTP session is initialized and open.
82
-
83
- :return: True if session exists and is not closed, False otherwise
84
- """
85
80
  session = self._provider.session
86
81
  return session is not None and not session.closed
87
82
 
88
83
  async def __aenter__(self) -> TonapiHttpClient:
89
- await self._provider.__aenter__()
84
+ await self._provider.connect()
90
85
  return self
91
86
 
92
87
  async def __aexit__(
@@ -95,7 +90,7 @@ class TonapiHttpClient(BaseClient):
95
90
  exc_value: t.Optional[BaseException],
96
91
  traceback: t.Optional[t.Any],
97
92
  ) -> None:
98
- await self._provider.__aexit__(exc_type, exc_value, traceback)
93
+ await self._provider.close()
99
94
 
100
95
  async def _send_boc(self, boc: str) -> None:
101
96
  payload = BlockchainMessagePayload(boc=boc)
@@ -161,14 +156,19 @@ class TonapiHttpClient(BaseClient):
161
156
  result = await self.provider.blockchain_account_method(
162
157
  address=address,
163
158
  method_name=method_name,
164
- args=encode_stack(stack or []),
159
+ args=encode_tonapi_stack(stack or []),
165
160
  )
166
- return decode_stack(result.stack or [])
161
+ if result.exit_code != 0:
162
+ raise RunGetMethodError(
163
+ address=address,
164
+ method_name=method_name,
165
+ exit_code=result.exit_code,
166
+ )
167
+
168
+ return decode_tonapi_stack(result.stack or [])
167
169
 
168
170
  async def connect(self) -> None:
169
- """Ensure that HTTP session is initialized."""
170
- await self._provider.ensure_session()
171
+ await self._provider.connect()
171
172
 
172
173
  async def close(self) -> None:
173
- """Close HTTP session if it is owned by the provider."""
174
174
  await self._provider.close()