Files
ccxt_with_mt5/ccxt/static_dependencies/ethereum/utils/conversions.py
lz_db 0fab423a18 add
2025-11-16 12:31:03 +08:00

191 lines
5.4 KiB
Python

from typing import (
Callable,
Optional,
TypeVar,
Union,
cast,
)
from ..typing import (
HexStr,
Primitives,
)
from .decorators import (
validate_conversion_arguments,
)
from .encoding import (
big_endian_to_int,
int_to_big_endian,
)
from .hexadecimal import (
add_0x_prefix,
decode_hex,
encode_hex,
is_hexstr,
remove_0x_prefix,
)
from .types import (
is_boolean,
is_integer,
is_string,
)
T = TypeVar("T")
@validate_conversion_arguments
def to_hex(
primitive: Optional[Primitives] = None,
hexstr: Optional[HexStr] = None,
text: Optional[str] = None,
) -> HexStr:
"""
Auto converts any supported value into its hex representation.
Trims leading zeros, as defined in:
https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding
"""
if hexstr is not None:
return add_0x_prefix(HexStr(hexstr.lower()))
if text is not None:
return encode_hex(text.encode("utf-8"))
if is_boolean(primitive):
return HexStr("0x1") if primitive else HexStr("0x0")
if isinstance(primitive, (bytes, bytearray)):
return encode_hex(primitive)
elif is_string(primitive):
raise TypeError(
"Unsupported type: The primitive argument must be one of: bytes,"
"bytearray, int or bool and not str"
)
if is_integer(primitive):
return HexStr(hex(cast(int, primitive)))
raise TypeError(
f"Unsupported type: '{repr(type(primitive))}'. Must be one of: bool, str, "
"bytes, bytearray or int."
)
@validate_conversion_arguments
def to_int(
primitive: Optional[Primitives] = None,
hexstr: Optional[HexStr] = None,
text: Optional[str] = None,
) -> int:
"""
Converts value to its integer representation.
Values are converted this way:
* primitive:
* bytes, bytearrays: big-endian integer
* bool: True => 1, False => 0
* hexstr: interpret hex as integer
* text: interpret as string of digits, like '12' => 12
"""
if hexstr is not None:
return int(hexstr, 16)
elif text is not None:
return int(text)
elif isinstance(primitive, (bytes, bytearray)):
return big_endian_to_int(primitive)
elif isinstance(primitive, str):
raise TypeError("Pass in strings with keyword hexstr or text")
elif isinstance(primitive, (int, bool)):
return int(primitive)
else:
raise TypeError(
"Invalid type. Expected one of int/bool/str/bytes/bytearray. Got "
f"{type(primitive)}"
)
@validate_conversion_arguments
def to_bytes(
primitive: Optional[Primitives] = None,
hexstr: Optional[HexStr] = None,
text: Optional[str] = None,
) -> bytes:
if is_boolean(primitive):
return b"\x01" if primitive else b"\x00"
elif isinstance(primitive, bytearray):
return bytes(primitive)
elif isinstance(primitive, bytes):
return primitive
elif is_integer(primitive):
return to_bytes(hexstr=to_hex(primitive))
elif hexstr is not None:
if len(hexstr) % 2:
hexstr = cast(HexStr, "0x0" + remove_0x_prefix(hexstr))
return decode_hex(hexstr)
elif text is not None:
return text.encode("utf-8")
raise TypeError(
"expected a bool, int, byte or bytearray in first arg, "
"or keyword of hexstr or text"
)
@validate_conversion_arguments
def to_text(
primitive: Optional[Primitives] = None,
hexstr: Optional[HexStr] = None,
text: Optional[str] = None,
) -> str:
if hexstr is not None:
return to_bytes(hexstr=hexstr).decode("utf-8")
elif text is not None:
return text
elif isinstance(primitive, str):
return to_text(hexstr=primitive)
elif isinstance(primitive, (bytes, bytearray)):
return primitive.decode("utf-8")
elif is_integer(primitive):
byte_encoding = int_to_big_endian(cast(int, primitive))
return to_text(byte_encoding)
raise TypeError("Expected an int, bytes, bytearray or hexstr.")
def text_if_str(
to_type: Callable[..., T], text_or_primitive: Union[bytes, int, str]
) -> T:
"""
Convert to a type, assuming that strings can be only unicode text (not a hexstr).
:param to_type function: takes the arguments (primitive, hexstr=hexstr, text=text),
eg~ to_bytes, to_text, to_hex, to_int, etc
:param text_or_primitive bytes, str, int: value to convert
"""
if isinstance(text_or_primitive, str):
return to_type(text=text_or_primitive)
else:
return to_type(text_or_primitive)
def hexstr_if_str(
to_type: Callable[..., T], hexstr_or_primitive: Union[bytes, int, str]
) -> T:
"""
Convert to a type, assuming that strings can be only hexstr (not unicode text).
:param to_type function: takes the arguments (primitive, hexstr=hexstr, text=text),
eg~ to_bytes, to_text, to_hex, to_int, etc
:param hexstr_or_primitive bytes, str, int: value to convert
"""
if isinstance(hexstr_or_primitive, str):
if remove_0x_prefix(HexStr(hexstr_or_primitive)) and not is_hexstr(
hexstr_or_primitive
):
raise ValueError(
"when sending a str, it must be a hex string. "
f"Got: {repr(hexstr_or_primitive)}"
)
return to_type(hexstr=hexstr_or_primitive)
else:
return to_type(hexstr_or_primitive)