Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions async_substrate_interface/async_substrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,7 @@ class AsyncSubstrateInterface(SubstrateMixin):
def __init__(
self,
url: str,
*,
use_remote_preset: bool = False,
auto_discover: bool = True,
ss58_format: Optional[int] = None,
Expand Down Expand Up @@ -1418,7 +1419,7 @@ async def encode_scale(
runtime: Optional[Runtime] = None,
) -> bytes:
"""
Helper function to encode arbitrary data into SCALE-bytes for given RUST type_string. If neither `block_hash`
Helper function to encode arbitrary data into SCALE-bytes for given type_string. If neither `block_hash`
nor `runtime` are supplied, the runtime of the current block will be used.

Args:
Expand Down Expand Up @@ -1572,10 +1573,6 @@ async def _get_runtime_for_version(
metadata = await self.get_block_metadata(
block_hash=runtime_block_hash, runtime_config=runtime_config
)
logger.debug(
f"Exported method Metadata_metadata_at_version is not found for {runtime_version}. "
f"This indicates the block is quite old, decoding for this block will use legacy Python decoding."
)
if metadata is None:
raise SubstrateRequestException(
f"No metadata for block '{runtime_block_hash}'"
Expand Down
2 changes: 2 additions & 0 deletions async_substrate_interface/substrate_addons.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class RetrySyncSubstrate(SubstrateInterface):
def __init__(
self,
url: str,
*,
use_remote_preset: bool = False,
fallback_chains: Optional[list[str]] = None,
retry_forever: bool = False,
Expand Down Expand Up @@ -251,6 +252,7 @@ class RetryAsyncSubstrate(AsyncSubstrateInterface):
def __init__(
self,
url: str,
*,
use_remote_preset: bool = False,
fallback_chains: Optional[list[str]] = None,
retry_forever: bool = False,
Expand Down
33 changes: 26 additions & 7 deletions async_substrate_interface/sync_substrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ class SubstrateInterface(SubstrateMixin):
def __init__(
self,
url: str,
*,
use_remote_preset: bool = False,
auto_discover: bool = True,
ss58_format: Optional[int] = None,
Expand Down Expand Up @@ -660,6 +661,30 @@ def _load_registry_at_block(self, block_hash: Optional[str], runtime_config=None
return None
return _decode_v15_metadata(inner_bytes, runtime_config=runtime_config)

def encode_scale(
self,
type_string,
value: Any,
block_hash: Optional[str] = None,
runtime: Optional[Runtime] = None,
) -> bytes:
"""
Helper function to encode arbitrary data into SCALE-bytes for given type_string. If neither `block_hash`
nor `runtime` are supplied, the runtime of the current block will be used.

Args:
type_string: the type string of the SCALE object for decoding
value: value to encode
block_hash: hash of the block where the desired runtime is located. Ignored if supplying `runtime`
runtime: the runtime to use for the scale encoding. If supplied, `block_hash` is ignored

Returns:
encoded bytes
"""
if runtime is None:
runtime = self.init_runtime(block_hash=block_hash)
return self._encode_scale(type_string, value, runtime=runtime)

def decode_scale(
self,
type_string: str,
Expand Down Expand Up @@ -798,10 +823,6 @@ def get_runtime_for_version(
)
else:
metadata = self.get_block_metadata(block_hash=runtime_block_hash)
logger.debug(
f"Exported method Metadata_metadata_at_version is not found for {runtime_version}. "
f"This indicates the block is quite old, decoding for this block will use legacy Python decoding."
)
if metadata is None:
raise SubstrateRequestException(
f"No metadata for block '{runtime_block_hash}'"
Expand Down Expand Up @@ -2371,7 +2392,7 @@ def _do_runtime_call_old(
param_data = runtime_call_def["encoder"](params, runtime)
param_hex = param_data.hex()
else:
param_data = self._encode_scale_legacy(
param_data = self._encode_scale_without_encoder(
runtime_call_def, params or [], runtime
)
param_hex = param_data.to_hex()
Expand Down Expand Up @@ -3239,5 +3260,3 @@ def close(self):
self.supports_rpc_method.cache_clear()
self._get_block_hash.cache_clear()
self._cached_get_block_number.cache_clear()

encode_scale = SubstrateMixin._encode_scale
90 changes: 12 additions & 78 deletions async_substrate_interface/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -991,72 +991,20 @@ def _encode_scale(
encoded bytes
"""
if value is None:
result = b"\x00"
else:
if not runtime:
runtime = self.runtime
assert runtime is not None
try:
vec_acct_id = (
f"scale_info::{runtime.registry_type_map['Vec<AccountId32>']}"
)
except KeyError:
vec_acct_id = "scale_info::152"
try:
optional_acct_u16 = f"scale_info::{runtime.registry_type_map['Option<(AccountId32, u16)>']}"
except KeyError:
optional_acct_u16 = "scale_info::579"

if type_string == "scale_info::0": # Is an AccountId
# encode string into AccountId
## AccountId is a composite type with one, unnamed field
return self._encode_account_id(value)

elif type_string == optional_acct_u16:
if value is None:
return b"\x00" # None

if not isinstance(value, (list, tuple)) or len(value) != 2:
raise ValueError("Expected tuple of (account_id, u16)")
account_id, u16_value = value

result = b"\x01"
result += self._encode_account_id(account_id)
result += u16_value.to_bytes(2, "little")
return result

elif type_string == vec_acct_id: # Vec<AccountId>
if not isinstance(value, (list, tuple)):
value = [value]

# Encode length
length = len(value)
if length < 64:
result = bytes([length << 2]) # Single byte mode
else:
raise ValueError("Vector length too large")

# Encode each AccountId
for account in value:
result += self._encode_account_id(account)
return result

if isinstance(value, ScaleType):
if value.data is not None and value.data.data is not None:
# Already encoded
return bytes(value.data.data)
else:
value = value.value # Unwrap the value of the type

result = bytes(
runtime.runtime_config.create_scale_object(type_string)
.encode(value)
.data
)
return result
return b"\x00"
if not runtime:
runtime = self.runtime
assert runtime is not None
if isinstance(value, ScaleType):
if value.data is not None and value.data.data is not None:
return bytes(value.data.data)
value = value.value
return bytes(
runtime.runtime_config.create_scale_object(type_string).encode(value).data
)

@staticmethod
def _encode_scale_legacy(
def _encode_scale_without_encoder(
call_definition: list[dict],
params: list[Any] | dict[str, Any],
runtime: Runtime,
Expand All @@ -1076,20 +1024,6 @@ def _encode_scale_legacy(

return param_data

@staticmethod
def _encode_account_id(account) -> bytes:
"""Encode an account ID into bytes.

Args:
account: Either bytes (already encoded) or SS58 string

Returns:
bytes: The encoded account ID
"""
if isinstance(account, bytes):
return account # Already encoded
return bytes.fromhex(ss58_decode(account, SS58_FORMAT)) # SS58 string

def generate_multisig_account(
self, signatories: list, threshold: int
) -> MultiAccountId:
Expand Down
Loading