tonutils 2.0.1b5__py3-none-any.whl → 2.0.1b7__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.
- tonutils/__meta__.py +1 -1
- tonutils/clients/__init__.py +10 -10
- tonutils/clients/adnl/balancer.py +135 -361
- tonutils/clients/adnl/client.py +35 -208
- tonutils/clients/adnl/mixin.py +268 -0
- tonutils/clients/adnl/provider/config.py +22 -7
- tonutils/clients/adnl/provider/provider.py +61 -16
- tonutils/clients/adnl/provider/transport.py +13 -4
- tonutils/clients/adnl/provider/workers/pinger.py +1 -1
- tonutils/clients/adnl/utils.py +5 -5
- tonutils/clients/base.py +61 -95
- tonutils/clients/http/__init__.py +11 -11
- tonutils/clients/http/balancer.py +103 -100
- tonutils/clients/http/clients/__init__.py +10 -10
- tonutils/clients/http/clients/chainstack.py +3 -3
- tonutils/clients/http/clients/quicknode.py +2 -3
- tonutils/clients/http/clients/tatum.py +4 -3
- tonutils/clients/http/clients/tonapi.py +20 -33
- tonutils/clients/http/clients/toncenter.py +64 -55
- tonutils/clients/http/{providers → provider}/__init__.py +4 -1
- tonutils/clients/http/{providers → provider}/base.py +140 -61
- tonutils/clients/http/{providers/toncenter → provider}/models.py +44 -2
- tonutils/clients/http/{providers/tonapi/provider.py → provider/tonapi.py} +8 -13
- tonutils/clients/http/{providers/toncenter/provider.py → provider/toncenter.py} +25 -21
- tonutils/clients/limiter.py +61 -59
- tonutils/clients/protocol.py +8 -8
- tonutils/contracts/base.py +32 -32
- tonutils/contracts/protocol.py +9 -9
- tonutils/contracts/wallet/base.py +7 -8
- tonutils/contracts/wallet/messages.py +4 -8
- tonutils/contracts/wallet/versions/v5.py +2 -2
- tonutils/exceptions.py +29 -13
- tonutils/tonconnect/bridge/__init__.py +0 -0
- tonutils/tonconnect/events.py +0 -0
- tonutils/tonconnect/models/__init__.py +0 -0
- tonutils/tonconnect/storage.py +0 -0
- tonutils/tonconnect/tonconnect.py +0 -0
- tonutils/tools/block_scanner/__init__.py +2 -5
- tonutils/tools/block_scanner/events.py +48 -7
- tonutils/tools/block_scanner/scanner.py +316 -222
- tonutils/tools/block_scanner/storage.py +11 -0
- tonutils/tools/status_monitor/monitor.py +6 -6
- tonutils/types.py +2 -2
- tonutils/utils.py +0 -48
- {tonutils-2.0.1b5.dist-info → tonutils-2.0.1b7.dist-info}/METADATA +3 -18
- {tonutils-2.0.1b5.dist-info → tonutils-2.0.1b7.dist-info}/RECORD +50 -51
- {tonutils-2.0.1b5.dist-info → tonutils-2.0.1b7.dist-info}/WHEEL +1 -1
- tonutils/clients/http/providers/response.py +0 -85
- tonutils/clients/http/providers/tonapi/__init__.py +0 -3
- tonutils/clients/http/providers/tonapi/models.py +0 -47
- tonutils/clients/http/providers/toncenter/__init__.py +0 -3
- tonutils/tools/block_scanner/annotations.py +0 -23
- tonutils/tools/block_scanner/dispatcher.py +0 -141
- tonutils/tools/block_scanner/traversal.py +0 -97
- tonutils/tools/block_scanner/where.py +0 -53
- {tonutils-2.0.1b5.dist-info → tonutils-2.0.1b7.dist-info}/entry_points.txt +0 -0
- {tonutils-2.0.1b5.dist-info → tonutils-2.0.1b7.dist-info}/licenses/LICENSE +0 -0
- {tonutils-2.0.1b5.dist-info → tonutils-2.0.1b7.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,7 @@ from itertools import cycle
|
|
|
10
10
|
from pytoniq_core import Transaction
|
|
11
11
|
|
|
12
12
|
from tonutils.clients.base import BaseClient
|
|
13
|
-
from tonutils.clients.http.clients.quicknode import
|
|
13
|
+
from tonutils.clients.http.clients.quicknode import QuicknodeClient
|
|
14
14
|
from tonutils.exceptions import (
|
|
15
15
|
BalancerError,
|
|
16
16
|
ClientError,
|
|
@@ -19,8 +19,9 @@ from tonutils.exceptions import (
|
|
|
19
19
|
ProviderResponseError,
|
|
20
20
|
RunGetMethodError,
|
|
21
21
|
ProviderTimeoutError,
|
|
22
|
+
NotConnectedError,
|
|
22
23
|
)
|
|
23
|
-
from tonutils.types import ClientType,
|
|
24
|
+
from tonutils.types import ClientType, ContractInfo, NetworkGlobalID
|
|
24
25
|
|
|
25
26
|
_T = t.TypeVar("_T")
|
|
26
27
|
|
|
@@ -50,8 +51,8 @@ class HttpBalancer(BaseClient):
|
|
|
50
51
|
|
|
51
52
|
def __init__(
|
|
52
53
|
self,
|
|
53
|
-
*,
|
|
54
54
|
network: NetworkGlobalID = NetworkGlobalID.MAINNET,
|
|
55
|
+
*,
|
|
55
56
|
clients: t.List[BaseClient],
|
|
56
57
|
request_timeout: float = 12.0,
|
|
57
58
|
) -> None:
|
|
@@ -60,27 +61,27 @@ class HttpBalancer(BaseClient):
|
|
|
60
61
|
|
|
61
62
|
Each supported HTTP client requires its own credentials or endpoint
|
|
62
63
|
configuration. You can obtain them from the corresponding provider:
|
|
63
|
-
-
|
|
64
|
+
- ToncenterClient:
|
|
64
65
|
API key available via Telegram bot: https://t.me/toncenter
|
|
65
|
-
-
|
|
66
|
+
- TonapiClient:
|
|
66
67
|
API key available on the Tonconsole website: https://tonconsole.com/
|
|
67
|
-
-
|
|
68
|
+
- ChainstackClient:
|
|
68
69
|
Personal endpoint available on: https://chainstack.com/
|
|
69
|
-
-
|
|
70
|
+
- QuicknodeClient:
|
|
70
71
|
Personal endpoint available on: https://www.quicknode.com/
|
|
71
|
-
-
|
|
72
|
+
- TatumClient:
|
|
72
73
|
API key available on: https://tatum.io/
|
|
73
74
|
|
|
74
75
|
:param network: Target TON network (mainnet or testnet)
|
|
75
76
|
:param clients: List of HTTP BaseClient instances to balance between
|
|
76
|
-
:param request_timeout: Maximum total time in seconds for a balancer
|
|
77
|
+
:param request_timeout: Maximum total time in seconds for a balancer method,
|
|
77
78
|
including all failover attempts across providers
|
|
78
79
|
"""
|
|
79
80
|
self.network = network
|
|
80
81
|
|
|
81
82
|
self._clients: t.List[BaseClient] = []
|
|
82
83
|
self._states: t.List[HttpClientState] = []
|
|
83
|
-
self.
|
|
84
|
+
self._init_clients(clients)
|
|
84
85
|
|
|
85
86
|
self._rr = cycle(self._clients)
|
|
86
87
|
|
|
@@ -89,31 +90,14 @@ class HttpBalancer(BaseClient):
|
|
|
89
90
|
|
|
90
91
|
self._request_timeout = request_timeout
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"""Validate and register input HTTP clients."""
|
|
97
|
-
for client in clients:
|
|
98
|
-
if client.TYPE != ClientType.HTTP:
|
|
99
|
-
raise ClientError(
|
|
100
|
-
"HttpBalancer can work only with HTTP clients, "
|
|
101
|
-
f"got {client.__class__.__name__}."
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
isinstance(client, QuicknodeHttpClient)
|
|
106
|
-
and self.network == NetworkGlobalID.TESTNET
|
|
107
|
-
):
|
|
108
|
-
raise ClientError(
|
|
109
|
-
"QuickNode HTTP client does not support testnet network."
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
client.network = self.network
|
|
113
|
-
state = HttpClientState(client=client)
|
|
93
|
+
@property
|
|
94
|
+
def connected(self) -> bool:
|
|
95
|
+
"""
|
|
96
|
+
Check whether at least one underlying client is connected.
|
|
114
97
|
|
|
115
|
-
|
|
116
|
-
|
|
98
|
+
:return: True if any client is connected, otherwise False
|
|
99
|
+
"""
|
|
100
|
+
return any(c.connected for c in self._clients)
|
|
117
101
|
|
|
118
102
|
@property
|
|
119
103
|
def provider(self) -> t.Any:
|
|
@@ -125,14 +109,17 @@ class HttpBalancer(BaseClient):
|
|
|
125
109
|
c = self._pick_client()
|
|
126
110
|
return c.provider
|
|
127
111
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
112
|
+
async def connect(self) -> None:
|
|
113
|
+
await asyncio.gather(
|
|
114
|
+
*(state.client.connect() for state in self._states),
|
|
115
|
+
return_exceptions=True,
|
|
116
|
+
)
|
|
132
117
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
118
|
+
async def close(self) -> None:
|
|
119
|
+
await asyncio.gather(
|
|
120
|
+
*(state.client.close() for state in self._states),
|
|
121
|
+
return_exceptions=True,
|
|
122
|
+
)
|
|
136
123
|
|
|
137
124
|
@property
|
|
138
125
|
def clients(self) -> t.Tuple[BaseClient, ...]:
|
|
@@ -154,7 +141,8 @@ class HttpBalancer(BaseClient):
|
|
|
154
141
|
return tuple(
|
|
155
142
|
state.client
|
|
156
143
|
for state in self._states
|
|
157
|
-
if state.
|
|
144
|
+
if state.client.connected
|
|
145
|
+
and (state.retry_after is None or state.retry_after <= now)
|
|
158
146
|
)
|
|
159
147
|
|
|
160
148
|
@property
|
|
@@ -171,24 +159,31 @@ class HttpBalancer(BaseClient):
|
|
|
171
159
|
if state.retry_after is not None and state.retry_after > now
|
|
172
160
|
)
|
|
173
161
|
|
|
174
|
-
|
|
175
|
-
"""
|
|
176
|
-
Enter async context manager and connect all underlying clients.
|
|
177
|
-
|
|
178
|
-
:return: Self instance with initialized connections
|
|
179
|
-
"""
|
|
180
|
-
await self.connect()
|
|
181
|
-
return self
|
|
182
|
-
|
|
183
|
-
async def __aexit__(
|
|
162
|
+
def _init_clients(
|
|
184
163
|
self,
|
|
185
|
-
|
|
186
|
-
exc_value: t.Optional[BaseException],
|
|
187
|
-
traceback: t.Optional[t.Any],
|
|
164
|
+
clients: t.List[BaseClient],
|
|
188
165
|
) -> None:
|
|
189
|
-
"""
|
|
190
|
-
|
|
191
|
-
|
|
166
|
+
"""Validate and register input HTTP clients."""
|
|
167
|
+
for client in clients:
|
|
168
|
+
if client.TYPE != ClientType.HTTP:
|
|
169
|
+
raise ClientError(
|
|
170
|
+
"HttpBalancer can work only with HTTP clients, "
|
|
171
|
+
f"got {client.__class__.__name__}."
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if (
|
|
175
|
+
isinstance(client, QuicknodeClient)
|
|
176
|
+
and self.network == NetworkGlobalID.TESTNET
|
|
177
|
+
):
|
|
178
|
+
raise ClientError(
|
|
179
|
+
"QuickNode HTTP client does not support testnet network."
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
client.network = self.network
|
|
183
|
+
state = HttpClientState(client=client)
|
|
184
|
+
|
|
185
|
+
self._clients.append(client)
|
|
186
|
+
self._states.append(state)
|
|
192
187
|
|
|
193
188
|
def _pick_client(self) -> BaseClient:
|
|
194
189
|
"""
|
|
@@ -201,8 +196,10 @@ class HttpBalancer(BaseClient):
|
|
|
201
196
|
|
|
202
197
|
:return: Selected BaseClient instance
|
|
203
198
|
"""
|
|
204
|
-
|
|
199
|
+
if not self.connected:
|
|
200
|
+
raise NotConnectedError(component=self.__class__.__name__)
|
|
205
201
|
|
|
202
|
+
alive = list(self.alive_clients)
|
|
206
203
|
height_candidates: t.List[
|
|
207
204
|
t.Tuple[
|
|
208
205
|
float,
|
|
@@ -223,7 +220,9 @@ class HttpBalancer(BaseClient):
|
|
|
223
220
|
height_candidates.append((wait, state.error_count, state))
|
|
224
221
|
|
|
225
222
|
if not height_candidates:
|
|
226
|
-
raise BalancerError(
|
|
223
|
+
raise BalancerError(
|
|
224
|
+
"http balancer has no available clients (all in cooldown or not connected)"
|
|
225
|
+
)
|
|
227
226
|
|
|
228
227
|
height_candidates.sort(key=lambda x: (x[0], x[1]))
|
|
229
228
|
best_wait, best_err, _ = height_candidates[0]
|
|
@@ -286,92 +285,107 @@ class HttpBalancer(BaseClient):
|
|
|
286
285
|
async def _with_failover(
|
|
287
286
|
self,
|
|
288
287
|
func: t.Callable[[BaseClient], t.Awaitable[_T]],
|
|
288
|
+
method: str,
|
|
289
289
|
) -> _T:
|
|
290
290
|
"""
|
|
291
|
-
Execute a client
|
|
291
|
+
Execute a client method with automatic failover.
|
|
292
292
|
|
|
293
293
|
Iterates through available clients until one succeeds
|
|
294
294
|
or all clients fail.
|
|
295
295
|
|
|
296
|
-
:param func: Callable performing an
|
|
296
|
+
:param func: Callable performing an method using a client
|
|
297
297
|
:return: Result of the successful invocation
|
|
298
298
|
"""
|
|
299
299
|
|
|
300
300
|
async def _run() -> _T:
|
|
301
|
+
if not self.connected:
|
|
302
|
+
raise NotConnectedError(
|
|
303
|
+
component=self.__class__.__name__,
|
|
304
|
+
operation=method,
|
|
305
|
+
)
|
|
306
|
+
|
|
301
307
|
last_exc: t.Optional[BaseException] = None
|
|
308
|
+
attempts = 0
|
|
302
309
|
|
|
303
310
|
for _ in range(len(self._clients)):
|
|
304
311
|
if not self.alive_clients:
|
|
305
312
|
break
|
|
306
313
|
|
|
307
314
|
client = self._pick_client()
|
|
315
|
+
attempts += 1
|
|
308
316
|
|
|
309
317
|
try:
|
|
310
318
|
result = await func(client)
|
|
311
|
-
|
|
312
319
|
except RunGetMethodError:
|
|
313
320
|
raise
|
|
314
321
|
except ProviderResponseError as e:
|
|
315
|
-
is_rate_limit
|
|
316
|
-
self._mark_error(client, is_rate_limit=is_rate_limit)
|
|
322
|
+
self._mark_error(client, is_rate_limit=(e.code == 429))
|
|
317
323
|
last_exc = e
|
|
318
324
|
continue
|
|
319
325
|
except (TransportError, ProviderError) as e:
|
|
320
326
|
self._mark_error(client, is_rate_limit=False)
|
|
321
327
|
last_exc = e
|
|
322
328
|
continue
|
|
329
|
+
else:
|
|
330
|
+
self._mark_success(client)
|
|
331
|
+
return result
|
|
323
332
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
raise last_exc
|
|
333
|
+
if last_exc is None:
|
|
334
|
+
raise BalancerError(
|
|
335
|
+
"http balancer has no available clients (all in cooldown or not connected)"
|
|
336
|
+
)
|
|
329
337
|
|
|
330
|
-
raise
|
|
338
|
+
raise BalancerError(
|
|
339
|
+
f"http failover exhausted after {attempts} attempt(s)"
|
|
340
|
+
) from last_exc
|
|
331
341
|
|
|
332
342
|
try:
|
|
333
343
|
return await asyncio.wait_for(_run(), timeout=self._request_timeout)
|
|
334
344
|
except asyncio.TimeoutError as exc:
|
|
335
345
|
raise ProviderTimeoutError(
|
|
336
346
|
timeout=self._request_timeout,
|
|
337
|
-
endpoint=
|
|
338
|
-
operation="
|
|
347
|
+
endpoint=self.__class__.__name__,
|
|
348
|
+
operation="request",
|
|
339
349
|
) from exc
|
|
340
350
|
|
|
341
|
-
async def
|
|
351
|
+
async def _send_message(self, boc: str) -> None:
|
|
342
352
|
async def _call(client: BaseClient) -> None:
|
|
343
|
-
return await client.
|
|
353
|
+
return await client._send_message(boc)
|
|
344
354
|
|
|
345
|
-
|
|
355
|
+
method = "send_message"
|
|
356
|
+
return await self._with_failover(_call, method)
|
|
346
357
|
|
|
347
|
-
async def
|
|
358
|
+
async def _get_config(self) -> t.Dict[int, t.Any]:
|
|
348
359
|
async def _call(client: BaseClient) -> t.Dict[int, t.Any]:
|
|
349
|
-
return await client.
|
|
360
|
+
return await client._get_config()
|
|
350
361
|
|
|
351
|
-
|
|
362
|
+
method = "get_config"
|
|
363
|
+
return await self._with_failover(_call, method)
|
|
352
364
|
|
|
353
|
-
async def
|
|
354
|
-
async def _call(client: BaseClient) ->
|
|
355
|
-
return await client.
|
|
365
|
+
async def _get_info(self, address: str) -> ContractInfo:
|
|
366
|
+
async def _call(client: BaseClient) -> ContractInfo:
|
|
367
|
+
return await client._get_info(address)
|
|
356
368
|
|
|
357
|
-
|
|
369
|
+
method = "get_info"
|
|
370
|
+
return await self._with_failover(_call, method)
|
|
358
371
|
|
|
359
|
-
async def
|
|
372
|
+
async def _get_transactions(
|
|
360
373
|
self,
|
|
361
374
|
address: str,
|
|
362
375
|
limit: int = 100,
|
|
363
376
|
from_lt: t.Optional[int] = None,
|
|
364
|
-
to_lt: int =
|
|
377
|
+
to_lt: t.Optional[int] = None,
|
|
365
378
|
) -> t.List[Transaction]:
|
|
366
379
|
async def _call(client: BaseClient) -> t.List[Transaction]:
|
|
367
|
-
return await client.
|
|
380
|
+
return await client._get_transactions(
|
|
368
381
|
address=address,
|
|
369
382
|
limit=limit,
|
|
370
383
|
from_lt=from_lt,
|
|
371
384
|
to_lt=to_lt,
|
|
372
385
|
)
|
|
373
386
|
|
|
374
|
-
|
|
387
|
+
method = "get_transactions"
|
|
388
|
+
return await self._with_failover(_call, method)
|
|
375
389
|
|
|
376
390
|
async def _run_get_method(
|
|
377
391
|
self,
|
|
@@ -386,16 +400,5 @@ class HttpBalancer(BaseClient):
|
|
|
386
400
|
stack=stack,
|
|
387
401
|
)
|
|
388
402
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
async def connect(self) -> None:
|
|
392
|
-
await asyncio.gather(
|
|
393
|
-
*(state.client.connect() for state in self._states),
|
|
394
|
-
return_exceptions=True,
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
async def close(self) -> None:
|
|
398
|
-
await asyncio.gather(
|
|
399
|
-
*(state.client.close() for state in self._states),
|
|
400
|
-
return_exceptions=True,
|
|
401
|
-
)
|
|
403
|
+
method = "run_get_method"
|
|
404
|
+
return await self._with_failover(_call, method)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
from .chainstack import
|
|
2
|
-
from .quicknode import
|
|
3
|
-
from .tatum import
|
|
4
|
-
from .tonapi import
|
|
5
|
-
from .toncenter import
|
|
1
|
+
from .chainstack import ChainstackClient
|
|
2
|
+
from .quicknode import QuicknodeClient
|
|
3
|
+
from .tatum import TatumClient
|
|
4
|
+
from .tonapi import TonapiClient
|
|
5
|
+
from .toncenter import ToncenterClient
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
8
|
+
"ChainstackClient",
|
|
9
|
+
"QuicknodeClient",
|
|
10
|
+
"TatumClient",
|
|
11
|
+
"TonapiClient",
|
|
12
|
+
"ToncenterClient",
|
|
13
13
|
]
|
|
@@ -2,16 +2,16 @@ import typing as t
|
|
|
2
2
|
|
|
3
3
|
from aiohttp import ClientSession
|
|
4
4
|
|
|
5
|
-
from tonutils.clients.http.clients.toncenter import
|
|
5
|
+
from tonutils.clients.http.clients.toncenter import ToncenterClient
|
|
6
6
|
from tonutils.types import NetworkGlobalID, RetryPolicy
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class ChainstackClient(ToncenterClient):
|
|
10
10
|
|
|
11
11
|
def __init__(
|
|
12
12
|
self,
|
|
13
|
-
*,
|
|
14
13
|
network: NetworkGlobalID,
|
|
14
|
+
*,
|
|
15
15
|
http_provider_url: str,
|
|
16
16
|
timeout: float = 10.0,
|
|
17
17
|
session: t.Optional[ClientSession] = None,
|
|
@@ -2,11 +2,11 @@ import typing as t
|
|
|
2
2
|
|
|
3
3
|
from aiohttp import ClientSession
|
|
4
4
|
|
|
5
|
-
from tonutils.clients.http.clients.toncenter import
|
|
5
|
+
from tonutils.clients.http.clients.toncenter import ToncenterClient
|
|
6
6
|
from tonutils.types import NetworkGlobalID, RetryPolicy
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class QuicknodeClient(ToncenterClient):
|
|
10
10
|
|
|
11
11
|
def __init__(
|
|
12
12
|
self,
|
|
@@ -33,7 +33,6 @@ class QuicknodeHttpClient(ToncenterHttpClient):
|
|
|
33
33
|
:param rps_period: Rate limit period in seconds.
|
|
34
34
|
:param retry_policy: Optional retry policy that defines per-error-code retry rules
|
|
35
35
|
"""
|
|
36
|
-
|
|
37
36
|
super().__init__(
|
|
38
37
|
network=NetworkGlobalID.MAINNET,
|
|
39
38
|
base_url=http_provider_url,
|
|
@@ -2,16 +2,16 @@ import typing as t
|
|
|
2
2
|
|
|
3
3
|
from aiohttp import ClientSession
|
|
4
4
|
|
|
5
|
-
from tonutils.clients.http.clients.toncenter import
|
|
5
|
+
from tonutils.clients.http.clients.toncenter import ToncenterClient
|
|
6
6
|
from tonutils.types import NetworkGlobalID, RetryPolicy
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class TatumClient(ToncenterClient):
|
|
10
10
|
|
|
11
11
|
def __init__(
|
|
12
12
|
self,
|
|
13
|
-
*,
|
|
14
13
|
network: NetworkGlobalID,
|
|
14
|
+
*,
|
|
15
15
|
api_key: str,
|
|
16
16
|
base_url: t.Optional[str] = None,
|
|
17
17
|
timeout: float = 10.0,
|
|
@@ -42,6 +42,7 @@ class TatumHttpClient(ToncenterHttpClient):
|
|
|
42
42
|
NetworkGlobalID.TESTNET: "https://ton-testnet.gateway.tatum.io",
|
|
43
43
|
}
|
|
44
44
|
base_url = base_url or urls.get(network)
|
|
45
|
+
|
|
45
46
|
super().__init__(
|
|
46
47
|
network=network,
|
|
47
48
|
api_key=api_key,
|
|
@@ -6,14 +6,14 @@ 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.
|
|
10
|
-
from tonutils.clients.http.
|
|
9
|
+
from tonutils.clients.http.provider.models import BlockchainMessagePayload
|
|
10
|
+
from tonutils.clients.http.provider.tonapi import TonapiHttpProvider
|
|
11
11
|
from tonutils.clients.http.utils import encode_tonapi_stack, decode_tonapi_stack
|
|
12
12
|
from tonutils.exceptions import ClientError, RunGetMethodError
|
|
13
13
|
from tonutils.types import (
|
|
14
14
|
ClientType,
|
|
15
15
|
ContractState,
|
|
16
|
-
|
|
16
|
+
ContractInfo,
|
|
17
17
|
NetworkGlobalID,
|
|
18
18
|
RetryPolicy,
|
|
19
19
|
)
|
|
@@ -23,15 +23,15 @@ from tonutils.utils import (
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class
|
|
26
|
+
class TonapiClient(BaseClient):
|
|
27
27
|
"""TON blockchain client using Tonapi HTTP API as transport."""
|
|
28
28
|
|
|
29
29
|
TYPE = ClientType.HTTP
|
|
30
30
|
|
|
31
31
|
def __init__(
|
|
32
32
|
self,
|
|
33
|
-
*,
|
|
34
33
|
network: NetworkGlobalID = NetworkGlobalID.MAINNET,
|
|
34
|
+
*,
|
|
35
35
|
api_key: str,
|
|
36
36
|
base_url: t.Optional[str] = None,
|
|
37
37
|
timeout: float = 10.0,
|
|
@@ -72,31 +72,25 @@ class TonapiHttpClient(BaseClient):
|
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
@property
|
|
75
|
-
def
|
|
76
|
-
return self._provider
|
|
77
|
-
|
|
78
|
-
@property
|
|
79
|
-
def is_connected(self) -> bool:
|
|
75
|
+
def connected(self) -> bool:
|
|
80
76
|
session = self._provider.session
|
|
81
77
|
return session is not None and not session.closed
|
|
82
78
|
|
|
83
|
-
|
|
79
|
+
@property
|
|
80
|
+
def provider(self) -> TonapiHttpProvider:
|
|
81
|
+
return self._provider
|
|
82
|
+
|
|
83
|
+
async def connect(self) -> None:
|
|
84
84
|
await self._provider.connect()
|
|
85
|
-
return self
|
|
86
85
|
|
|
87
|
-
async def
|
|
88
|
-
self,
|
|
89
|
-
exc_type: t.Optional[t.Type[BaseException]],
|
|
90
|
-
exc_value: t.Optional[BaseException],
|
|
91
|
-
traceback: t.Optional[t.Any],
|
|
92
|
-
) -> None:
|
|
86
|
+
async def close(self) -> None:
|
|
93
87
|
await self._provider.close()
|
|
94
88
|
|
|
95
|
-
async def
|
|
89
|
+
async def _send_message(self, boc: str) -> None:
|
|
96
90
|
payload = BlockchainMessagePayload(boc=boc)
|
|
97
91
|
return await self.provider.blockchain_message(payload=payload)
|
|
98
92
|
|
|
99
|
-
async def
|
|
93
|
+
async def _get_config(self) -> t.Dict[int, t.Any]:
|
|
100
94
|
result = await self.provider.blockchain_config()
|
|
101
95
|
|
|
102
96
|
if result.raw is None:
|
|
@@ -106,10 +100,10 @@ class TonapiHttpClient(BaseClient):
|
|
|
106
100
|
config_slice = config_cell.begin_parse()
|
|
107
101
|
return parse_stack_config(config_slice)
|
|
108
102
|
|
|
109
|
-
async def
|
|
103
|
+
async def _get_info(self, address: str) -> ContractInfo:
|
|
110
104
|
result = await self.provider.blockchain_account(address)
|
|
111
105
|
|
|
112
|
-
contract_info =
|
|
106
|
+
contract_info = ContractInfo(
|
|
113
107
|
balance=result.balance,
|
|
114
108
|
state=ContractState(result.status),
|
|
115
109
|
last_transaction_lt=result.last_transaction_lt,
|
|
@@ -122,21 +116,20 @@ class TonapiHttpClient(BaseClient):
|
|
|
122
116
|
|
|
123
117
|
return contract_info
|
|
124
118
|
|
|
125
|
-
async def
|
|
119
|
+
async def _get_transactions(
|
|
126
120
|
self,
|
|
127
121
|
address: str,
|
|
128
122
|
limit: int = 100,
|
|
129
123
|
from_lt: t.Optional[int] = None,
|
|
130
|
-
to_lt: int =
|
|
124
|
+
to_lt: t.Optional[int] = None,
|
|
131
125
|
) -> t.List[Transaction]:
|
|
132
|
-
if from_lt is not None
|
|
133
|
-
from_lt += 1
|
|
126
|
+
before_lt = from_lt + 1 if from_lt is not None else None
|
|
134
127
|
|
|
135
128
|
result = await self.provider.blockchain_account_transactions(
|
|
136
129
|
address=address,
|
|
137
130
|
limit=limit,
|
|
138
131
|
after_lt=to_lt,
|
|
139
|
-
before_lt=
|
|
132
|
+
before_lt=before_lt,
|
|
140
133
|
)
|
|
141
134
|
|
|
142
135
|
transactions = []
|
|
@@ -166,9 +159,3 @@ class TonapiHttpClient(BaseClient):
|
|
|
166
159
|
)
|
|
167
160
|
|
|
168
161
|
return decode_tonapi_stack(result.stack or [])
|
|
169
|
-
|
|
170
|
-
async def connect(self) -> None:
|
|
171
|
-
await self._provider.connect()
|
|
172
|
-
|
|
173
|
-
async def close(self) -> None:
|
|
174
|
-
await self._provider.close()
|