tonutils 2.0.1b7__py3-none-any.whl → 2.0.1b8__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/__init__.py CHANGED
@@ -1,17 +1,12 @@
1
- from . import (
2
- clients,
3
- contracts,
4
- exceptions,
5
- types,
6
- utils,
7
- )
8
- from .__meta__ import __version__
1
+ # Copyright (c) 2024 Shon Ness
2
+ #
3
+ # This source code is licensed under the MIT License found in the
4
+ # LICENSE file in the root directory of this source tree.
9
5
 
10
6
  __all__ = [
7
+ "__uri__",
11
8
  "__version__",
12
- "clients",
13
- "contracts",
14
- "exceptions",
15
- "types",
16
- "utils",
17
9
  ]
10
+
11
+ __version__ = "2.0.1b8"
12
+ __uri__ = "https://github.com/nessshon/tonutils"
tonutils/cli.py CHANGED
@@ -2,7 +2,7 @@ import argparse
2
2
  import asyncio
3
3
  import typing as t
4
4
 
5
- from tonutils.__meta__ import __version__
5
+ from tonutils import __version__
6
6
  from tonutils.clients.adnl.provider.config import (
7
7
  get_mainnet_global_config,
8
8
  get_testnet_global_config,
@@ -1,33 +1,20 @@
1
- import json
2
- import urllib.request
3
- from pathlib import Path
4
- from urllib.error import HTTPError, URLError
5
-
6
1
  from pydantic import ValidationError
7
2
 
8
3
  from tonutils.clients.adnl.provider.models import GlobalConfig
4
+ from tonutils.utils import load_json
9
5
 
10
6
 
11
7
  def load_global_config(source: str) -> GlobalConfig:
8
+ """
9
+ Fetch global configuration from source.
10
+
11
+ :return: Parsed GlobalConfig instance
12
+ """
12
13
  try:
13
- if source.startswith(("http://", "https://")):
14
- with urllib.request.urlopen(source) as r:
15
- data = json.loads(r.read().decode("utf-8"))
16
- else:
17
- data = json.loads(Path(source).read_text(encoding="utf-8"))
14
+ data = load_json(source)
18
15
  return GlobalConfig.model_validate(data)
19
- except HTTPError as e:
20
- raise RuntimeError(f"Config fetch failed: {e} ({source})") from e
21
- except URLError as e:
22
- raise RuntimeError(f"Config fetch failed: {e.reason} ({source})") from e
23
- except json.JSONDecodeError as e:
24
- raise RuntimeError(f"Config JSON is invalid: {e.msg} ({source})") from e
25
16
  except ValidationError as e:
26
17
  raise RuntimeError(f"Config validation failed: {e} ({source})") from e
27
- except OSError as e:
28
- raise RuntimeError(f"Config read failed: {e} ({source})") from e
29
- except Exception as e:
30
- raise RuntimeError(f"Config load failed: {e} ({source})") from e
31
18
 
32
19
 
33
20
  def get_mainnet_global_config() -> GlobalConfig:
@@ -127,6 +127,8 @@ from .vanity import (
127
127
  from .versions import ContractVersion
128
128
  from .wallet import (
129
129
  BaseMessageBuilder,
130
+ BaseWalletData,
131
+ BaseWalletParams,
130
132
  EncryptedTextCommentBody,
131
133
  ExternalMessage,
132
134
  InternalMessage,
@@ -194,6 +196,7 @@ __all__ = [
194
196
  "ALLOWED_DNS_ZONES",
195
197
  "BaseContract",
196
198
  "BaseMessageBuilder",
199
+ "BaseWalletData",
197
200
  "ChangeDNSRecordBody",
198
201
  "CONTRACT_CODES",
199
202
  "ContractProtocol",
@@ -296,7 +296,7 @@ class TeleItemContent(TlbScheme):
296
296
  """
297
297
  Initialize item content.
298
298
 
299
- :param nft_content: Off-chain NFT metadata (image, description, etc)
299
+ :param nft_content: Off-chain NFT metadata (image, description, etc.)
300
300
  :param dns: DNS records for the username/domain
301
301
  :param token_info: Token name and domain information
302
302
  """
@@ -34,6 +34,7 @@ from .methods import (
34
34
  seqno_get_method,
35
35
  )
36
36
  from .params import (
37
+ BaseWalletParams,
37
38
  WalletHighloadV2Params,
38
39
  WalletHighloadV3Params,
39
40
  WalletPreprocessedV2Params,
@@ -46,6 +47,7 @@ from .params import (
46
47
  )
47
48
  from .protocol import WalletProtocol
48
49
  from .tlb import (
50
+ BaseWalletData,
49
51
  EncryptedTextCommentBody,
50
52
  OutActionSendMsg,
51
53
  TextCommentBody,
@@ -81,6 +83,8 @@ __all__ = [
81
83
  "VALID_MNEMONIC_LENGTHS",
82
84
  "BaseMessageBuilder",
83
85
  "BaseWallet",
86
+ "BaseWalletData",
87
+ "BaseWalletParams",
84
88
  "EncryptedTextCommentBody",
85
89
  "ExternalMessage",
86
90
  "InternalMessage",
@@ -203,7 +203,7 @@ class WalletV5SubwalletID:
203
203
  subwallet_number: int = 0,
204
204
  workchain: WorkchainID = WorkchainID.BASECHAIN,
205
205
  version: int = 0,
206
- network_global_id: NetworkGlobalID = NetworkGlobalID.MAINNET,
206
+ network: NetworkGlobalID = NetworkGlobalID.MAINNET,
207
207
  ) -> None:
208
208
  """
209
209
  Initialize Wallet v5 subwallet ID.
@@ -211,19 +211,19 @@ class WalletV5SubwalletID:
211
211
  :param subwallet_number: Subwallet number (0-32767)
212
212
  :param workchain: Target workchain (default: BASECHAIN)
213
213
  :param version: Wallet version identifier (default: 0)
214
- :param network_global_id: Network identifier (default: MAINNET)
214
+ :param network: Network identifier (default: MAINNET)
215
215
  """
216
216
  self.subwallet_number = subwallet_number
217
217
  self.workchain = workchain
218
218
  self.version = version
219
- self.network_global_id = network_global_id
219
+ self.network = network
220
220
 
221
221
  def pack(self) -> int:
222
222
  """
223
223
  Pack subwallet ID components into 32-bit integer.
224
224
 
225
225
  Format: (1 << 31) | (workchain << 23) | (version << 15) | subwallet_number
226
- XORed with network_global_id for network isolation.
226
+ XORed with network for network isolation.
227
227
 
228
228
  :return: Packed 32-bit subwallet ID
229
229
  """
@@ -232,22 +232,22 @@ class WalletV5SubwalletID:
232
232
  ctx |= (self.workchain & 0xFF) << 23
233
233
  ctx |= (self.version & 0xFF) << 15
234
234
  ctx |= self.subwallet_number & 0x7FFF
235
- return ctx ^ (self.network_global_id & 0xFFFFFFFF)
235
+ return ctx ^ (self.network & 0xFFFFFFFF)
236
236
 
237
237
  @classmethod
238
238
  def unpack(
239
239
  cls,
240
240
  value: int,
241
- network_global_id: NetworkGlobalID,
241
+ network: NetworkGlobalID,
242
242
  ) -> WalletV5SubwalletID:
243
243
  """
244
244
  Unpack 32-bit integer into subwallet ID components.
245
245
 
246
246
  :param value: Packed 32-bit subwallet ID
247
- :param network_global_id: Network identifier for XOR decoding
247
+ :param network: Network identifier for XOR decoding
248
248
  :return: Unpacked WalletV5SubwalletID instance
249
249
  """
250
- ctx = (value ^ network_global_id) & 0xFFFFFFFF
250
+ ctx = (value ^ network) & 0xFFFFFFFF
251
251
 
252
252
  subwallet_number = ctx & 0x7FFF
253
253
  version = (ctx >> 15) & 0xFF
@@ -258,7 +258,7 @@ class WalletV5SubwalletID:
258
258
  subwallet_number=subwallet_number,
259
259
  workchain=WorkchainID(workchain),
260
260
  version=version,
261
- network_global_id=network_global_id,
261
+ network=network,
262
262
  )
263
263
 
264
264
  def __repr__(self) -> str:
@@ -298,7 +298,7 @@ class WalletV5BetaData(BaseWalletData):
298
298
 
299
299
  :param builder: Cell builder to store to
300
300
  """
301
- builder.store_int(self.subwallet_id.network_global_id, 32)
301
+ builder.store_int(self.subwallet_id.network, 32)
302
302
  builder.store_int(self.subwallet_id.workchain, 8)
303
303
  builder.store_uint(self.subwallet_id.version, 8)
304
304
  builder.store_uint(self.subwallet_id.subwallet_number, 32)
@@ -328,7 +328,7 @@ class WalletV5BetaData(BaseWalletData):
328
328
  :return: Loaded WalletV5SubwalletID
329
329
  """
330
330
  return WalletV5SubwalletID(
331
- network_global_id=NetworkGlobalID(cs.load_int(32)),
331
+ network=NetworkGlobalID(cs.load_int(32)),
332
332
  workchain=WorkchainID(cs.load_int(8)),
333
333
  version=cs.load_uint(8),
334
334
  subwallet_number=cs.load_uint(32),
@@ -398,20 +398,22 @@ class WalletV5Data(BaseWalletData):
398
398
  return cell.end_cell()
399
399
 
400
400
  @classmethod
401
- def deserialize(cls, cs: Slice, network_global_id: NetworkGlobalID) -> WalletV5Data:
401
+ def deserialize(
402
+ cls,
403
+ cs: Slice,
404
+ network: NetworkGlobalID = NetworkGlobalID.MAINNET,
405
+ ) -> WalletV5Data:
402
406
  """
403
407
  Deserialize wallet data from Cell slice.
404
408
 
405
409
  :param cs: Cell slice to deserialize from
406
- :param network_global_id: Network ID for unpacking subwallet_id
410
+ :param network: Network ID for unpacking subwallet_id
407
411
  :return: Deserialized WalletV5Data instance
408
412
  """
409
413
  return cls(
410
414
  is_signature_allowed=cs.load_bool(),
411
415
  seqno=cs.load_uint(32),
412
- subwallet_id=WalletV5SubwalletID.unpack(
413
- cs.load_uint(32), network_global_id
414
- ),
416
+ subwallet_id=WalletV5SubwalletID.unpack(cs.load_uint(32), network),
415
417
  public_key=PublicKey(cs.load_bytes(32)),
416
418
  plugins=cs.load_maybe_ref(),
417
419
  )
@@ -63,7 +63,7 @@ class _WalletV5(
63
63
  cls._validate_config_type(config)
64
64
 
65
65
  if config.subwallet_id is None:
66
- config.subwallet_id = WalletV5SubwalletID(network_global_id=client.network)
66
+ config.subwallet_id = WalletV5SubwalletID(network=client.network)
67
67
 
68
68
  return super().from_private_key(client, private_key, workchain, config)
69
69
 
@@ -163,7 +163,7 @@ class WalletV5Beta(
163
163
 
164
164
  cell = begin_cell()
165
165
  cell.store_uint(params.op_code, 32)
166
- cell.store_int(subwallet_id.network_global_id, 32)
166
+ cell.store_int(subwallet_id.network, 32)
167
167
  cell.store_int(subwallet_id.workchain, 8)
168
168
  cell.store_uint(subwallet_id.version, 8)
169
169
  cell.store_uint(subwallet_id.subwallet_number, 32)
@@ -222,11 +222,11 @@ class WalletV5R1(
222
222
  if not (self._info and self._info.data):
223
223
  raise StateNotLoadedError(self, missing="state_data")
224
224
 
225
- network_global_id = (
225
+ network = (
226
226
  NetworkGlobalID.TESTNET if self.client.network else NetworkGlobalID.MAINNET
227
227
  )
228
228
  cs = self._info.data.begin_parse()
229
- return self._data_model.deserialize(cs, network_global_id)
229
+ return self._data_model.deserialize(cs, network)
230
230
 
231
231
  async def _build_msg_cell(
232
232
  self,
tonutils/exceptions.py CHANGED
@@ -23,26 +23,9 @@ class TonutilsError(Exception):
23
23
 
24
24
 
25
25
  class TransportError(TonutilsError):
26
- """Transport-level failure with structured context.
26
+ """Transport-level failure (connect/handshake/send/recv)."""
27
27
 
28
- Covers: TCP connect, ADNL handshake, send/recv, crypto failures.
29
-
30
- :param endpoint: Endpoint identifier (URL or "host:port").
31
- :param operation: What was attempted ("connect", "handshake", "send", "recv")
32
- :param reason: Why it failed ("timeout 2.0s", "connection refused", etc.)
33
- """
34
-
35
- endpoint: str
36
- operation: str
37
- reason: str
38
-
39
- def __init__(
40
- self,
41
- *,
42
- endpoint: str,
43
- operation: str,
44
- reason: str,
45
- ) -> None:
28
+ def __init__(self, *, endpoint: str, operation: str, reason: str) -> None:
46
29
  self.endpoint = endpoint
47
30
  self.operation = operation
48
31
  self.reason = reason
@@ -50,23 +33,19 @@ class TransportError(TonutilsError):
50
33
 
51
34
 
52
35
  class ProviderError(TonutilsError):
53
- """Raise on provider-level failures (protocol, parsing, session/state)."""
36
+ """Provider-level failure (protocol/parsing/backend/state)."""
54
37
 
55
38
 
56
39
  class ClientError(TonutilsError):
57
- """Raise on client misuse, validation errors, or unsupported operations."""
40
+ """Client misuse, validation errors, or unsupported operations."""
58
41
 
59
42
 
60
43
  class BalancerError(TonutilsError):
61
- """Raise on balancer failures (no alive backends, failover exhausted)."""
44
+ """Balancer failure (no alive backends, failover exhausted)."""
62
45
 
63
46
 
64
47
  class NotConnectedError(TonutilsError, RuntimeError):
65
- """Raise when an operation requires an active connection."""
66
-
67
- component: str
68
- endpoint: t.Optional[str]
69
- operation: t.Optional[str]
48
+ """Raised when an operation requires an active connection."""
70
49
 
71
50
  def __init__(
72
51
  self,
@@ -74,112 +53,69 @@ class NotConnectedError(TonutilsError, RuntimeError):
74
53
  component: str = "client",
75
54
  endpoint: t.Optional[str] = None,
76
55
  operation: t.Optional[str] = None,
77
- hint: t.Optional[str] = None,
78
56
  ) -> None:
79
57
  self.component = component
80
58
  self.endpoint = endpoint
81
59
  self.operation = operation
82
60
 
83
- if hint is None:
84
- hint = "Call connect() first or use an async context manager (`async with ...`)."
85
-
61
+ op = f"cannot `{operation}`: " if operation else ""
86
62
  where = f" ({endpoint})" if endpoint else ""
87
- prefix = f"cannot `{operation}`: " if operation else ""
88
- super().__init__(f"{prefix}{component} is not connected{where}. {hint}")
63
+ super().__init__(f"{op}{component} is not connected{where}")
89
64
 
90
65
 
91
66
  class ProviderTimeoutError(ProviderError, asyncio.TimeoutError):
92
- """Raise when a provider operation exceeds its timeout.
93
-
94
- :param timeout: Timeout in seconds.
95
- :param endpoint: Endpoint identifier (URL or host:port).
96
- :param operation: Operation label (e.g. "request", "connect").
97
- """
98
-
99
- timeout: float
100
- endpoint: str
101
- operation: str
67
+ """Provider operation exceeded its timeout."""
102
68
 
103
69
  def __init__(self, *, timeout: float, endpoint: str, operation: str) -> None:
104
- self.timeout = timeout
70
+ self.timeout = float(timeout)
105
71
  self.endpoint = endpoint
106
72
  self.operation = operation
107
- super().__init__(f"{operation} timed out after {timeout}s ({endpoint})")
73
+ super().__init__(f"{operation} timed out after {self.timeout}s ({endpoint})")
108
74
 
109
75
 
110
76
  class ProviderResponseError(ProviderError):
111
- """Raise when a backend returns an error response.
112
-
113
- :param code: Backend code (HTTP status or lite-server code).
114
- :param message: Backend error description.
115
- :param endpoint: Endpoint identifier (URL or host:port).
116
- """
117
-
118
- code: int
119
- message: str
120
- endpoint: str
77
+ """Backend returned an error response."""
121
78
 
122
79
  def __init__(self, *, code: int, message: str, endpoint: str) -> None:
123
- self.code = code
80
+ self.code = int(code)
124
81
  self.message = message
125
82
  self.endpoint = endpoint
126
- super().__init__(f"request failed: {code} {message} ({endpoint})")
83
+ super().__init__(f"request failed: {self.code} {self.message} ({endpoint})")
127
84
 
128
85
 
129
86
  class RetryLimitError(ProviderError):
130
- """Raise when retry policy is exhausted for a matched rule.
131
-
132
- :param attempts: Attempts already performed for the matched rule.
133
- :param max_attempts: Maximum attempts allowed by the matched rule.
134
- :param last_error: Last provider error that triggered a retry.
135
- """
136
-
137
- attempts: int
138
- max_attempts: int
139
- last_error: ProviderError
87
+ """Retry policy exhausted."""
140
88
 
141
89
  def __init__(
142
90
  self,
143
- *,
144
91
  attempts: int,
145
92
  max_attempts: int,
146
93
  last_error: ProviderError,
147
94
  ) -> None:
148
- self.attempts = attempts
149
- self.max_attempts = max_attempts
95
+ self.attempts = int(attempts)
96
+ self.max_attempts = int(max_attempts)
150
97
  self.last_error = last_error
151
- super().__init__(
152
- f"retry limit reached ({attempts}/{max_attempts}): {last_error}"
153
- )
98
+ ratio = f"{self.attempts}/{self.max_attempts}"
99
+ super().__init__(f"retry limit reached {ratio}: {last_error}")
154
100
 
155
101
 
156
102
  class ContractError(ClientError):
157
- """Raise when a contract wrapper operation fails.
158
-
159
- :param target: Contract instance or contract class related to the failure.
160
- :param message: Human-readable error message.
161
- """
162
-
163
- target: t.Any
164
- message: str
103
+ """Contract wrapper operation failed."""
165
104
 
166
- def __init__(self, target: t.Any, message: str) -> None:
105
+ def __init__(self, target: t.Any, details: str) -> None:
167
106
  self.target = target
168
- self.message = message
169
- name = (
170
- target.__name__ if isinstance(target, type) else target.__class__.__name__
171
- )
172
- super().__init__(f"{name} failed: {message}")
107
+ self.details = details
173
108
 
109
+ if isinstance(target, type):
110
+ name = target.__name__
111
+ else:
112
+ name = target.__class__.__name__
174
113
 
175
- class StateNotLoadedError(ContractError):
176
- """Raise when a contract wrapper requires state that is not loaded.
114
+ super().__init__(f"{name} failed: {details}")
177
115
 
178
- :param contract: Contract instance related to the failure.
179
- :param missing: Missing field name (e.g. "info", "state_data").
180
- """
181
116
 
182
- missing: str
117
+ class StateNotLoadedError(ContractError):
118
+ """Contract wrapper requires state that is not loaded."""
183
119
 
184
120
  def __init__(self, contract: t.Any, *, missing: str) -> None:
185
121
  self.missing = missing
@@ -188,23 +124,14 @@ class StateNotLoadedError(ContractError):
188
124
 
189
125
 
190
126
  class RunGetMethodError(ClientError):
191
- """Raise when a contract get-method returns a non-zero TVM exit code.
192
-
193
- :param address: Contract address (string form).
194
- :param method_name: Get-method name.
195
- :param exit_code: TVM exit code.
196
- """
197
-
198
- address: str
199
- method_name: str
200
- exit_code: int
127
+ """Contract get-method returned a non-zero TVM exit code."""
201
128
 
202
- def __init__(self, *, address: str, method_name: str, exit_code: int) -> None:
129
+ def __init__(self, *, address: str, exit_code: int, method_name: str) -> None:
203
130
  self.address = address
131
+ self.exit_code = int(exit_code)
204
132
  self.method_name = method_name
205
- self.exit_code = exit_code
206
133
  super().__init__(
207
- f"get-method `{method_name}` failed: exit code {exit_code} ({address})"
134
+ f"get-method `{method_name}` failed: exit code {self.exit_code} ({address})"
208
135
  )
209
136
 
210
137
 
tonutils/types.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import base64
4
+ import re
4
5
  import typing as t
6
+ from contextlib import suppress
5
7
  from dataclasses import dataclass
6
8
  from enum import Enum
7
9
 
@@ -280,22 +282,34 @@ class Binary:
280
282
  return self._size
281
283
 
282
284
  def _parse(self, value: t.Any) -> bytes:
283
- """Parse input value into bytes."""
284
285
  if isinstance(value, bytes):
285
286
  return value
286
287
  if isinstance(value, int):
287
288
  length = max(1, (value.bit_length() + 7) // 8)
288
289
  return value.to_bytes(length, "big")
290
+
289
291
  if isinstance(value, str):
290
292
  s = value.strip()
293
+
294
+ # 0x... hex
291
295
  if s.lower().startswith("0x"):
292
- return int(s, 16).to_bytes(self._size, "big")
293
- try:
294
- return base64.b64decode(s)
295
- except (Exception,):
296
- n = int(s, 10)
297
- length = max(1, (n.bit_length() + 7) // 8)
298
- return n.to_bytes(length, "big")
296
+ return bytes.fromhex(s[2:])
297
+
298
+ # plain hex (common case for publicKey)
299
+ if len(s) % 2 == 0 and re.compile(r"^[0-9a-fA-F]+$").fullmatch(s):
300
+ if len(s) == self._size * 2:
301
+ return bytes.fromhex(s)
302
+
303
+ # base64 (strict)
304
+ with suppress(Exception):
305
+ b = base64.b64decode(s, validate=True)
306
+ return b
307
+
308
+ # decimal int as string fallback
309
+ n = int(s, 10)
310
+ length = max(1, (n.bit_length() + 7) // 8)
311
+ return n.to_bytes(length, "big")
312
+
299
313
  raise ValueError(f"Invalid binary type: {type(value).__name__}.")
300
314
 
301
315
  @property
tonutils/utils.py CHANGED
@@ -5,9 +5,13 @@ import binascii
5
5
  import decimal
6
6
  import hashlib
7
7
  import hmac
8
+ import json
8
9
  import os
9
10
  import time
10
11
  import typing as t
12
+ import urllib.request
13
+ from pathlib import Path
14
+ from urllib.error import HTTPError, URLError
11
15
 
12
16
  from Cryptodome.Cipher import AES
13
17
  from nacl.bindings import (
@@ -40,6 +44,7 @@ __all__ = [
40
44
  "cell_to_hex",
41
45
  "decode_dns_name",
42
46
  "encode_dns_name",
47
+ "load_json",
43
48
  "maybe_stack_addr",
44
49
  "norm_stack_cell",
45
50
  "norm_stack_num",
@@ -387,15 +392,15 @@ class TextCipher:
387
392
  if isinstance(payload, bytes):
388
393
  return payload[:32], payload[32:48], payload[48:]
389
394
  elif isinstance(payload, str):
395
+ # Try hex first; if that fails, try base64.
390
396
  try:
391
- payload = bytes.fromhex(payload)
397
+ data = bytes.fromhex(payload)
392
398
  except ValueError:
393
- pass
394
- try:
395
- payload = base64.b64decode(payload, validate=True)
396
- except (binascii.Error, ValueError):
397
- raise ValueError("Invalid payload encoding: not hex or base64.")
398
- return payload[:32], payload[32:48], payload[48:]
399
+ try:
400
+ data = base64.b64decode(payload, validate=True)
401
+ except (binascii.Error, ValueError):
402
+ raise ValueError("Invalid payload encoding: not hex or base64.")
403
+ return data[:32], data[32:48], data[48:]
399
404
 
400
405
  cell = EncryptedTextCommentBody.deserialize(payload.begin_parse())
401
406
  return cell.pub_xor, cell.msg_key, cell.ciphertext
@@ -501,3 +506,38 @@ class TextCipher:
501
506
 
502
507
  comment = dec_data[padding_size:]
503
508
  return comment.decode()
509
+
510
+
511
+ def load_json(source: str, timeout: float = 5.0) -> t.Any:
512
+ """
513
+ Load and parse JSON from a URL or a local file.
514
+
515
+ :param source: URL or file path
516
+ :param timeout: Network timeout in seconds
517
+ :return: Parsed JSON object
518
+ """
519
+ try:
520
+ if source.startswith(("http://", "https://")):
521
+ req = urllib.request.Request(
522
+ source,
523
+ method="GET",
524
+ headers={
525
+ "User-Agent": "tonutils (+https://github.com/nessshon/tonutils)",
526
+ "Accept": "application/json,text/plain,*/*",
527
+ },
528
+ )
529
+ with urllib.request.urlopen(req, timeout=timeout) as r:
530
+ return json.loads(r.read().decode("utf-8"))
531
+
532
+ return json.loads(Path(source).read_text(encoding="utf-8"))
533
+
534
+ except HTTPError as e:
535
+ raise RuntimeError(f"JSON fetch failed: {e} ({source})") from e
536
+ except URLError as e:
537
+ raise RuntimeError(f"JSON fetch failed: {e.reason} ({source})") from e
538
+ except json.JSONDecodeError as e:
539
+ raise RuntimeError(f"JSON is invalid: {e.msg} ({source})") from e
540
+ except OSError as e:
541
+ raise RuntimeError(f"JSON read failed: {e} ({source})") from e
542
+ except Exception as e:
543
+ raise RuntimeError(f"JSON load failed: {e} ({source})") from e
@@ -1,13 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tonutils
3
- Version: 2.0.1b7
3
+ Version: 2.0.1b8
4
4
  Summary: Tonutils is a high-level, object-oriented Python library designed to facilitate seamless interactions with the TON blockchain.
5
5
  Author: nessshon
6
6
  Maintainer: nessshon
7
7
  License-Expression: MIT
8
8
  Project-URL: Homepage, https://github.com/nessshon/tonutils/
9
- Project-URL: Documentation, https://nessshon.github.io/tonutils/
10
- Project-URL: Repository, https://github.com/nessshon/tonutils/
11
9
  Project-URL: Examples, https://github.com/nessshon/tonutils/tree/main/examples/
12
10
  Keywords: AsyncIO,TON,TON blockchain,The Open Network,blockchain,crypto,smart contracts
13
11
  Classifier: Development Status :: 4 - Beta
@@ -1,10 +1,9 @@
1
- tonutils/__init__.py,sha256=ueJrDkU1JBlZiX0q8roQfzYOZY62Of_CiHZlxIIQFO0,228
2
- tonutils/__meta__.py,sha256=pnBUMwEiE3Otdl5pbcYM2yy-aa5fTg4NFSVHb1eXhG4,24
3
- tonutils/cli.py,sha256=WGir-ihgPuKTgKGmhjPZeKk9wgsm64jiJciOnVlsdco,2645
4
- tonutils/exceptions.py,sha256=64TSU9LCTSEH70o1-qHTNIL-HXpxXv1xcsRklizJEGU,6929
1
+ tonutils/__init__.py,sha256=L-T_WYdCKByp5eNttc_saSyUumBCiupZNruAbbGxvxY,280
2
+ tonutils/cli.py,sha256=67QAK8NWr4BpC-zMua_iFs7w_O-6LrpSRR3jrhtHmpU,2636
3
+ tonutils/exceptions.py,sha256=atvVuRhbTMWMkQPzSFHVRQG2VRspsOEFN4mYZgt5mjk,4959
5
4
  tonutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- tonutils/types.py,sha256=amHTnp1Mp7Z8G_mWscu10hufeIVjYbkaxKEYixXoKH4,14532
7
- tonutils/utils.py,sha256=10R2eGjzNMEN9glngcJhZ36O76ec55NodH-c8IgN6yw,15062
5
+ tonutils/types.py,sha256=XSW-9yOyN5xWtH3hbbNuD2N9AJTrTZZ3Cwqfx63S1Sc,14850
6
+ tonutils/utils.py,sha256=MjyZfz8i8r016vJxFCVj-59UnFaCxbJqWTZZARNPrVM,16542
8
7
  tonutils/clients/__init__.py,sha256=F0aPLPOI8-HcqcweeCSG_fr2VArxiZxfsWHuzHDwE4k,521
9
8
  tonutils/clients/base.py,sha256=FHKDJCUDNoLUuE4W0J3SGEtgJudW_6zFIAaP6zTKr_4,8207
10
9
  tonutils/clients/limiter.py,sha256=lSUo6AhEuWUlo0y2sUc-P4YltRI2zjbicelYKkMTrls,3896
@@ -15,7 +14,7 @@ tonutils/clients/adnl/client.py,sha256=2tUr6YxxoNXdc0c0iRkyT1xpZkIKIM-DwgNTKJn-q
15
14
  tonutils/clients/adnl/mixin.py,sha256=GVakCui_0yso0VCOsycZsvq55Ng6JyIdotrQ_0bIohM,8260
16
15
  tonutils/clients/adnl/utils.py,sha256=pyrU0vLuR9qnOFlHtheOfch8u1FP4Q_5Ah2UuX_m7Jk,5129
17
16
  tonutils/clients/adnl/provider/__init__.py,sha256=lLwFEEgCifMlYozwSzQbvq97R6Du7TzgGFnTgUnk4gw,63
18
- tonutils/clients/adnl/provider/config.py,sha256=jMeCdg14KjS6Zh7sgcjop4LdLtrHA91PbWm1zD1mnt0,1655
17
+ tonutils/clients/adnl/provider/config.py,sha256=phylkEdmKtfJWp9VgOYpzTuG4SUIHV7G42rYfqcgfDU,943
19
18
  tonutils/clients/adnl/provider/models.py,sha256=3dn4oZv7PIgiwlP2lGs0O6VckC19ejxCFlSPhzU0wX8,4537
20
19
  tonutils/clients/adnl/provider/provider.py,sha256=J7wIGtv_ZeS5j6AdYafAQ0m3q4z8Sd5V_LcpfeSi7JM,25633
21
20
  tonutils/clients/adnl/provider/transport.py,sha256=bT_a7h0nCBGKCfPNOg79CW04UIdyZCfQ_7-nFvpfWK0,11241
@@ -38,7 +37,7 @@ tonutils/clients/http/provider/base.py,sha256=JqhtFwLLGyTbTVXO6V8_TJf-nZtYjFrSXR
38
37
  tonutils/clients/http/provider/models.py,sha256=HFB3xL9B7DHdahw9-qCdMv0pVCvOsVaklTM0Yz0pU-M,3510
39
38
  tonutils/clients/http/provider/tonapi.py,sha256=IgEiz8VwZ3rKLAVB8Aq3RxUDcC2HMD2NIX4m_j1tk6U,3495
40
39
  tonutils/clients/http/provider/toncenter.py,sha256=m_wLe778ibe0LMFBV2iIuULbrSxSoPOd-OmTNSXm5lE,3461
41
- tonutils/contracts/__init__.py,sha256=KZm3_rQwoY0kO6zFGpPcjrN9XBTjDDvpB2dfaHjwjjw,9561
40
+ tonutils/contracts/__init__.py,sha256=vlvrI_Xsa-tt4dZW3YMz2ezJLL50T8OweHmxpFc3Mkc,9625
42
41
  tonutils/contracts/base.py,sha256=CvTKWVJEvm07HThCenJ0T-QDYne_CgRD02Nyo29Exfg,10332
43
42
  tonutils/contracts/codes.py,sha256=1Sbbs_izHZHd-i1cjWHRP8VN3Xc2BDPr4pnjojj6mZc,37741
44
43
  tonutils/contracts/opcodes.py,sha256=niPd-pDmtXiEpYX8xvorFmd7O4vkY0i8nX71e3iaJ1s,1001
@@ -63,19 +62,19 @@ tonutils/contracts/telegram/__init__.py,sha256=nr0O0rzr2UJu1KrtrqbBqLOuUfMSLSWo4
63
62
  tonutils/contracts/telegram/collection.py,sha256=CM3T_8eWmrO_Ovq-crZgesfJ4h6JV4DhyggjV6nBY-U,3925
64
63
  tonutils/contracts/telegram/item.py,sha256=DhNs5WFjhI1vqMfUg-cm311JTIvSaznc3aTqtLOCr9w,3887
65
64
  tonutils/contracts/telegram/methods.py,sha256=rcZsEYTDwbT92NFhBhs-8TYruQWmxYgF6KElvaRnJPg,4403
66
- tonutils/contracts/telegram/tlb.py,sha256=QRYijGDT0YGjRRjYiHjbgyeSxMP9RiSmRvmdhh6itS4,17848
65
+ tonutils/contracts/telegram/tlb.py,sha256=TXbQfQT7OEjeoahHAavvZBCEq79qFWuVsyL5rllMXiI,17849
67
66
  tonutils/contracts/vanity/__init__.py,sha256=6LvJQxpmtrE3-ju44IsrkYQTx4HSq8nRb3fLyJFwrgE,288
68
67
  tonutils/contracts/vanity/models.py,sha256=B6W1TN4CyrMs4SfBDAjuQ8QP-wn5QFhNpcSzO99DCbY,815
69
68
  tonutils/contracts/vanity/tlb.py,sha256=gcNYEGPWMUHYbg_Je9QbBUlmVXF5RmobL-FoCMCF1HA,1078
70
69
  tonutils/contracts/vanity/vanity.py,sha256=uYH1zybcOTQQBPciUFxAS6wksBwawKx06YES2tLuguI,1242
71
- tonutils/contracts/wallet/__init__.py,sha256=Ho3-3C09JNk53ySnQaUnNscchfCtbUdyA2kpWAFzOf4,3295
70
+ tonutils/contracts/wallet/__init__.py,sha256=vNw1tj8sN9jgd_FK2Bclphqi3x5nyFHozHttldWRLFg,3383
72
71
  tonutils/contracts/wallet/base.py,sha256=Ue0WMTtjQ55s3o1_67KHdqGIsjjFrgdoLjCiMwA1y1Q,15806
73
72
  tonutils/contracts/wallet/configs.py,sha256=yQfuCEGL_fBuc5qGJ93rPIUATTR8V1wpYscgrWb7cEQ,4082
74
73
  tonutils/contracts/wallet/messages.py,sha256=MDzM3HdRYIc_vhmsjeHZcF9z1zpcfSdSh3_s4ecSx7o,13610
75
74
  tonutils/contracts/wallet/methods.py,sha256=x7aPt71v3PUFNYHStWQrjLK7_SWPC2MiTG0c15X5TRI,10732
76
75
  tonutils/contracts/wallet/params.py,sha256=hqinZJmhWiZUywDcmolvRxB0HYJgMAPDWYJiTmgjZ7w,4569
77
76
  tonutils/contracts/wallet/protocol.py,sha256=DCu3CNbcZJ_wwROEK3GlpnwxNY2ZLdE0D2Z23WiyVDY,6200
78
- tonutils/contracts/wallet/tlb.py,sha256=rS1oZJ6jvi7zGOuPPN6S7RajQaLaUsMm-VqO09dys8I,21871
77
+ tonutils/contracts/wallet/tlb.py,sha256=RzUcHTzFPnf19ajt_WP3_17c4xKVCu_c8cLOgBZBsy8,21738
79
78
  tonutils/contracts/wallet/versions/__init__.py,sha256=DOHAEpx1mOlHdyTg2K0Mj8ZkcjabSdpLvIQY9_Pk6gw,592
80
79
  tonutils/contracts/wallet/versions/hw.py,sha256=9kc_mlCegBo1O4_MvRQGLE3ZqLHsvqGN2F1IgnJeBOE,8348
81
80
  tonutils/contracts/wallet/versions/pp.py,sha256=QfTqd4TAJHFMuatIwe0lg80uixno2JqSZFmQboxhca0,3793
@@ -83,13 +82,7 @@ tonutils/contracts/wallet/versions/v1.py,sha256=BYRWXdM0OlSeCfayHAUBj_wM4bb3WTVY
83
82
  tonutils/contracts/wallet/versions/v2.py,sha256=pwrlan-utZo_WmnzDwSbnzV8ibkPEWx2WU1uOjkdrrA,2452
84
83
  tonutils/contracts/wallet/versions/v3.py,sha256=d7cM8wjmW-1H7jGuY3AuUd7eTY3wq9ZYpJ4f5OeYX08,2470
85
84
  tonutils/contracts/wallet/versions/v4.py,sha256=2sAsjJ8_3oYAj5JwWH3PiMyoGbgl6-f7-p6T5X7MGTI,2713
86
- tonutils/contracts/wallet/versions/v5.py,sha256=fvOEdKLVbHGj1jyxd05xLuerZgZD1s3sMa0FnZ1Ds2U,8872
87
- tonutils/tonconnect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
- tonutils/tonconnect/events.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- tonutils/tonconnect/storage.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- tonutils/tonconnect/tonconnect.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
- tonutils/tonconnect/bridge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
- tonutils/tonconnect/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ tonutils/contracts/wallet/versions/v5.py,sha256=-JjuFgLjKQIlKjr5RsUZ-qMWlkUs1y02t1n_4QSQkrk,8832
93
86
  tonutils/tools/__init__.py,sha256=QYOVuGY50FFkWlgIvHc2RPU3xiEWSbwnwZ6wuZPQnCA,102
94
87
  tonutils/tools/block_scanner/__init__.py,sha256=yYARZYo4LlePo7DWrrYlejGE4NLInfNeU9Ipln8oFFA,209
95
88
  tonutils/tools/block_scanner/events.py,sha256=02K85PR3Jfe6qK-Ve1Mbukk4AWkxCJv1d-C-0tGdH_s,1881
@@ -99,9 +92,9 @@ tonutils/tools/status_monitor/__init__.py,sha256=QnMlA0IDLtCGgXsEgB9q3EJTBo2s5js
99
92
  tonutils/tools/status_monitor/console.py,sha256=UX3BzjjzeS_nKFGg4NkZJpu9fR_IAJZdQUMz0HcJCdg,5036
100
93
  tonutils/tools/status_monitor/models.py,sha256=yHuiEuij4h2kVoOK3sbhNq6SwiGDW_evZmzUwMy1GQs,608
101
94
  tonutils/tools/status_monitor/monitor.py,sha256=OXs-J5RCUp4VbnBZuGd-4LythGUGakxwGSYM1Ipw-4s,10065
102
- tonutils-2.0.1b7.dist-info/licenses/LICENSE,sha256=fG-yM-8DSkOTaJ558P7uF5PNXBmineVO9-HC12YbIxs,1060
103
- tonutils-2.0.1b7.dist-info/METADATA,sha256=l-mwZF3Co184WoWPM8cnuk3Nj9-Doj3i3wy3P87oEPY,3519
104
- tonutils-2.0.1b7.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
105
- tonutils-2.0.1b7.dist-info/entry_points.txt,sha256=qijo1cqvbbzLVbXp-PCYh19Pgmd7duH6yljmnUPd55I,47
106
- tonutils-2.0.1b7.dist-info/top_level.txt,sha256=-7H_mGl8S9HKQrkUiTLmEbtMM-knzRzd_a0cZZnuZIU,9
107
- tonutils-2.0.1b7.dist-info/RECORD,,
95
+ tonutils-2.0.1b8.dist-info/licenses/LICENSE,sha256=fG-yM-8DSkOTaJ558P7uF5PNXBmineVO9-HC12YbIxs,1060
96
+ tonutils-2.0.1b8.dist-info/METADATA,sha256=vvOdq7wiIe6Y-RlNzLykw1JvThRvJgTQAFzqLJNLt8w,3391
97
+ tonutils-2.0.1b8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
98
+ tonutils-2.0.1b8.dist-info/entry_points.txt,sha256=qijo1cqvbbzLVbXp-PCYh19Pgmd7duH6yljmnUPd55I,47
99
+ tonutils-2.0.1b8.dist-info/top_level.txt,sha256=-7H_mGl8S9HKQrkUiTLmEbtMM-knzRzd_a0cZZnuZIU,9
100
+ tonutils-2.0.1b8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tonutils/__meta__.py DELETED
@@ -1 +0,0 @@
1
- __version__ = "2.0.1b7"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes