Module tronpytool.common.abi
Expand source code
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------
import re
from collections import (
namedtuple,
)
from typing import Tuple
import binascii
import eth_abi
import itertools
from eth_abi import (
encoding,
decoding
)
from eth_abi.codec import ABICodec
from eth_abi.registry import (
BaseEquals,
registry as default_registry, ABIRegistry,
)
from eth_utils import to_tuple
from trx_utils import (
decode_hex,
is_bytes,
is_text,
to_text
)
from tronpytool.common.formatters import recursive_map
from tronpytool.common.toolz import (
curry,
partial,
pipe,
)
from tronpytool.exceptions import FallbackNotFound
DYNAMIC_TYPES = ['bytes', 'string']
INT_SIZES = range(8, 257, 8)
BYTES_SIZES = range(1, 33)
UINT_TYPES = ['uint{0}'.format(i) for i in INT_SIZES]
INT_TYPES = ['int{0}'.format(i) for i in INT_SIZES]
BYTES_TYPES = ['bytes{0}'.format(i) for i in BYTES_SIZES] + ['bytes32.byte']
STATIC_TYPES = list(itertools.chain(
['address', 'bool'],
UINT_TYPES,
INT_TYPES,
BYTES_TYPES,
))
BASE_TYPE_REGEX = '|'.join((
_type + '(?![a-z0-9])'
for _type
in itertools.chain(STATIC_TYPES, DYNAMIC_TYPES)
))
SUB_TYPE_REGEX = (
r'\['
'[0-9]*'
r'\]'
)
TYPE_REGEX = (
'^'
'(?:{base_type})'
'(?:(?:{sub_type})*)?'
'$'
).format(
base_type=BASE_TYPE_REGEX,
sub_type=SUB_TYPE_REGEX,
)
NAME_REGEX = (
'[a-zA-Z_]'
'[a-zA-Z0-9_]*'
)
ENUM_REGEX = (
'^'
'{lib_name}'
r'\.'
'{enum_name}'
'$'
).format(lib_name=NAME_REGEX, enum_name=NAME_REGEX)
END_BRACKETS_OF_ARRAY_TYPE_REGEX = r"\[[^]]*\]$"
NAME_REGEX = (
'[a-zA-Z_]'
'[a-zA-Z0-9_]*'
)
ARRAY_REGEX = (
"^"
"[a-zA-Z0-9_]+"
"({sub_type})+"
"$"
).format(sub_type=SUB_TYPE_REGEX)
RES_CODE = dict(
SUCCESS=0,
SIGERROR=1,
CONTRACT_VALIDATE_ERROR=2,
CONTRACT_EXE_ERROR=3,
BANDWITH_ERROR=4,
DUP_TRANSACTION_ERROR=5,
TAPOS_ERROR=6,
TOO_BIG_TRANSACTION_ERROR=7,
TRANSACTION_EXPIRATION_ERROR=8,
SERVER_BUSY=9,
NO_CONNECTION=10,
NOT_ENOUGH_EFFECTIVE_CONNECTION=11,
OTHER_ERROR=20
)
def filter_by_argument_name(argument_names, contract_abi):
return [
abi
for abi in contract_abi
if set(argument_names).intersection(
get_abi_input_names(abi)
) == set(argument_names)
]
try:
from eth_abi.abi import (
process_type,
collapse_type,
)
except ImportError:
from eth_abi.grammar import (
parse as parse_type_string,
normalize as normalize_type_string,
TupleType,
)
def process_type(type_str):
normalized_type_str = normalize_type_string(type_str)
abi_type = parse_type_string(normalized_type_str)
if isinstance(abi_type, TupleType):
type_str_repr = repr(type_str)
if type_str != normalized_type_str:
type_str_repr = '{} (normalized to {})'.format(
type_str_repr,
repr(normalized_type_str),
)
raise ValueError(
"Cannot process type {}: tuple types not supported".format(
type_str_repr,
)
)
abi_type.validate()
sub = abi_type.sub
if isinstance(sub, tuple):
sub = 'x'.join(map(str, sub))
elif isinstance(sub, int):
sub = str(sub)
else:
sub = ''
arrlist = abi_type.arrlist
if isinstance(arrlist, tuple):
arrlist = list(map(list, arrlist))
else:
arrlist = []
return abi_type.base, sub, arrlist
def collapse_type(base, sub, arrlist):
return base + str(sub) + ''.join(map(repr, arrlist))
"""abi function filter processor"""
def filter_by_type(_type, contract_abi) -> list:
return [abi for abi in contract_abi if abi['type'] == _type]
def filter_by_name(name, contract_abi) -> list:
return [
abi
for abi
in contract_abi
if (
abi['type'] not in ('fallback', 'constructor') and
abi['name'] == name
)
]
def get_abi_input_types(abi) -> list:
if 'inputs' not in abi and abi['type'] == 'fallback':
return []
else:
return [arg['type'] for arg in abi['inputs']]
def get_abi_output_types(abi) -> list:
if abi['type'] == 'fallback':
return []
else:
return [arg['type'] for arg in abi['outputs']]
def get_abi_input_names(abi) -> list:
if 'inputs' not in abi and abi['type'] == 'fallback':
return []
else:
return [arg['name'] for arg in abi['inputs']]
def length_of_array_type(abi_type) -> any:
if not is_array_type(abi_type):
raise ValueError(
"Cannot parse length of nonarray abi-type: {0}".format(abi_type)
)
inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip("[]")
if not inner_brackets:
return None
else:
return int(inner_brackets)
def get_fallback_func_abi(contract_abi):
fallback_abis = filter_by_type('fallback', contract_abi)
if fallback_abis:
return fallback_abis[0]
else:
raise FallbackNotFound("No fallback function was found in the contract ABI.")
def fallback_func_abi_exists(contract_abi) -> list:
return filter_by_type('fallback', contract_abi)
def get_constructor_abi(contract_abi):
candidates = [
abi for abi in contract_abi if abi['type'] == 'constructor'
]
if len(candidates) == 1:
return candidates[0]
elif len(candidates) == 0:
return None
elif len(candidates) > 1:
raise ValueError("Found multiple constructors.")
def is_recognized_type(abi_type) -> bool:
return bool(re.match(TYPE_REGEX, abi_type))
def is_bool_type(abi_type) -> bool:
return abi_type == 'bool'
def is_uint_type(abi_type) -> bool:
return abi_type in UINT_TYPES
def is_int_type(abi_type) -> bool:
return abi_type in INT_TYPES
def is_address_type(abi_type) -> bool:
return abi_type == 'address'
def is_bytes_type(abi_type) -> bool:
return abi_type in BYTES_TYPES + ['bytes']
def is_string_type(abi_type) -> bool:
return abi_type == 'string'
@curry
def is_length(target_length, value) -> bool:
return len(value) == target_length
def size_of_type(abi_type):
"""
Returns size in bits of abi_type
"""
if 'string' in abi_type:
return None
if 'byte' in abi_type:
return None
if '[' in abi_type:
return None
if abi_type == 'bool':
return 8
if abi_type == 'address':
return 160
return int(re.sub(r"\D", "", abi_type))
def is_array_type(abi_type) -> bool:
return bool(re.match(ARRAY_REGEX, abi_type))
def sub_type_of_array_type(abi_type):
if not is_array_type(abi_type):
raise ValueError(
"Cannot parse subtype of nonarray abi-type: {0}".format(abi_type)
)
return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1)
def is_probably_enum(abi_type):
return bool(re.match(ENUM_REGEX, abi_type))
@to_tuple
def normalize_event_input_types(abi_args):
for arg in abi_args:
if is_recognized_type(arg['type']):
yield arg
elif is_probably_enum(arg['type']):
yield {k: 'uint8' if k == 'type' else v for k, v in arg.items()}
else:
yield arg
def abi_to_signature(abi):
function_signature = "{fn_name}({fn_input_types})".format(
fn_name=abi['name'],
fn_input_types=','.join([
arg['type'] for arg in normalize_event_input_types(abi.get('inputs', []))
]),
)
return function_signature
def filter_by_argument_count(num_arguments, contract_abi) -> list:
return [
abi
for abi
in contract_abi
if len(abi['inputs']) == num_arguments
]
def filter_by_encodability(args, kwargs, contract_abi) -> list:
return [
function_abi
for function_abi
in contract_abi
if check_if_arguments_can_be_encoded(function_abi, args, kwargs)
]
class AcceptsHexStrMixin:
def validate_value(self, value):
if is_text(value):
try:
value = decode_hex(value)
except binascii.Error:
self.invalidate_value(
value,
msg='invalid hex string',
)
super().validate_value(value)
from eth_abi.base import parse_type_str
from eth_abi.decoding import Fixed32ByteSizeDecoder
from eth_abi.encoding import Fixed32ByteSizeEncoder
from tronpytool.common.key import to_base58check_address, is_address, to_tvm_address
class TronAddressDecoder(Fixed32ByteSizeDecoder):
value_bit_size = 20 * 8
is_big_endian = True
decoder_fn = staticmethod(to_base58check_address)
@parse_type_str("address")
def from_type_str(cls, abi_type, registry):
return cls()
class TronAddressEncoder(Fixed32ByteSizeEncoder):
value_bit_size = 20 * 8
encode_fn = staticmethod(to_tvm_address)
is_big_endian = True
@classmethod
def validate_value(cls, value):
if not is_address(value):
cls.invalidate_value(value)
def validate(self):
super().validate()
if self.value_bit_size != 20 * 8:
raise ValueError("Addresses must be 160 bits in length")
@parse_type_str("address")
def from_type_str(cls, abi_type, registry):
return cls()
# interface
class ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder):
pass
# interface
class BytesEncoder(AcceptsHexStrMixin, encoding.BytesEncoder):
pass
class TextStringEncoder(encoding.TextStringEncoder):
@classmethod
def validate_value(cls, value):
if is_bytes(value):
try:
value = to_text(value)
except UnicodeDecodeError:
cls.invalidate_value(
value,
msg='not decodable as unicode string',
)
super().validate_value(value)
def check_if_arguments_can_be_encoded(function_abi, args, kwargs):
try:
arguments = merge_args_and_kwargs(function_abi, args, kwargs)
except TypeError:
return False
if len(function_abi.get('inputs', [])) != len(arguments):
return False
types = get_abi_input_types(function_abi)
return all(
is_encodable(_type, arg)
for _type, arg in zip(types, arguments)
)
def merge_args_and_kwargs(function_abi, args, kwargs):
if len(args) + len(kwargs) != len(function_abi.get('inputs', [])):
raise TypeError(
"Incorrect argument count. Expected '{0}'. Got '{1}'".format(
len(function_abi['inputs']),
len(args) + len(kwargs),
)
)
if not kwargs:
return args
args_as_kwargs = {
arg_abi['name']: arg
for arg_abi, arg in zip(function_abi['inputs'], args)
}
duplicate_keys = set(args_as_kwargs).intersection(kwargs.keys())
if duplicate_keys:
raise TypeError(
"{fn_name}() got multiple values for argument(s) '{dups}'".format(
fn_name=function_abi['name'],
dups=', '.join(duplicate_keys),
)
)
sorted_arg_names = [arg_abi['name'] for arg_abi in function_abi['inputs']]
unknown_kwargs = {key for key in kwargs.keys() if key not in sorted_arg_names}
if unknown_kwargs:
if function_abi.get('name'):
raise TypeError(
"{fn_name}() got unexpected keyword argument(s) '{dups}'".format(
fn_name=function_abi.get('name'),
dups=', '.join(unknown_kwargs),
)
)
# show type instead of name in the error message incase key 'name' is missing.
raise TypeError(
"Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format(
_type=function_abi.get('type'),
dups=', '.join(unknown_kwargs),
)
)
sorted_args = list(zip(
*sorted(
itertools.chain(kwargs.items(), args_as_kwargs.items()),
key=lambda kv: sorted_arg_names.index(kv[0])
)
))
if sorted_args:
return sorted_args[1]
else:
return tuple()
def abi_sub_tree(data_type, data_value):
if data_type is None:
return ABITypedData([None, data_value])
try:
base, sub, arrlist = data_type
except ValueError:
base, sub, arrlist = process_type(data_type)
collapsed = collapse_type(base, sub, arrlist)
if arrlist:
sub_type = (base, sub, arrlist[:-1])
return ABITypedData([
collapsed,
[
abi_sub_tree(sub_type, sub_value)
for sub_value in data_value
],
])
else:
return ABITypedData([collapsed, data_value])
@curry
def map_abi_data(normalizers, types, data):
"""
This function will apply normalizers to your data, in the
context of the relevant types. Each normalizer is in the format:
def normalizer(datatype, data):
# Conditionally modify data
return (datatype, data)
Where datatype is a valid ABI type string, like "uint".
In case of an array, like "bool[2]", normalizer will receive `data`
as an iterable of typed data, like `[("bool", True), ("bool", False)]`.
Internals
---
This is accomplished by:
1. Decorating the data tree with types
2. Recursively mapping each of the normalizers to the data
3. Stripping the types back out of the tree
"""
pipeline = itertools.chain(
[abi_data_tree(types)],
map(data_tree_map, normalizers),
[partial(recursive_map, strip_abi_type)],
)
return pipe(data, *pipeline)
@curry
def abi_data_tree(types, data) -> list:
"""Decorate the data tree with pairs of (type, data). The pair tuple is actually an
ABITypedData, but can be accessed as a tuple.
Examples:
>>> abi_data_tree(types=["bool[2]", "uint"], data=[[True, False], 0])
Returns:
[("bool[2]", [("bool", True), ("bool", False)]), ("uint256", 0)]
"""
return [
abi_sub_tree(data_type, data_value)
for data_type, data_value
in zip(types, data)
]
@curry
def data_tree_map(func, data_tree):
"""
Map func to every ABITypedData element in the tree. func will
receive two args: abi_type, and data
"""
def map_to_typed_data(elements):
if isinstance(elements, ABITypedData) and elements.abi_type is not None:
return ABITypedData(func(*elements))
else:
return elements
return recursive_map(map_to_typed_data, data_tree)
class ABITypedData(namedtuple('ABITypedData', 'abi_type, data')):
"""
This class marks data as having a certain ABI-type.
>>> a1 = ABITypedData(['address', addr1])
>>> a2 = ABITypedData(['address', addr2])
>>> addrs = ABITypedData(['address[]', [a1, a2])
You can access the fields using tuple() interface, or with
attributes:
>>> assert a1.abi_type == a1[0]
>>> assert a1.data == a1[1]
Unlike a typical `namedtuple`, you initialize with a single
positional argument that is iterable, to match the init
interface of all other relevant collections.
"""
def __new__(cls, iterable):
return super().__new__(cls, *iterable)
def strip_abi_type(elements):
if isinstance(elements, ABITypedData):
return elements.data
else:
return elements
def method_result_handler(r: dict) -> Tuple[bool, str, str]:
if "result" in r["result"]:
transID = r["transaction"]["txID"]
resultcode = r["result"]["result"]
if resultcode:
if "constant_result" in r:
return True, r["constant_result"], transID
else:
return True, "", transID
else:
print("======")
print(r)
print("======")
return True, "", ""
elif "code" in r["result"]:
resultcode = r["result"]["code"]
if RES_CODE.get(resultcode) > 0:
return False, resultcode, r["result"]["message"]
else:
return True, resultcode, r["result"]["message"]
# We make a copy here just to make sure that eth-abi's default registry is not
# affected by our custom encoder subclasses
registry = default_registry.copy()
def tron_patch_ethereum_types(_registry: ABIRegistry):
_registry.register(
BaseEquals('address'),
TronAddressEncoder,
TronAddressDecoder,
label='address',
)
_registry.register(
BaseEquals('trcToken'),
eth_abi.encoding.UnsignedIntegerEncoder,
eth_abi.decoding.UnsignedIntegerDecoder,
label='trcToken',
)
_registry.register(
BaseEquals('trc20'),
eth_abi.encoding.UnsignedIntegerEncoder,
eth_abi.decoding.UnsignedIntegerDecoder,
label='trc20',
)
_registry.register(
BaseEquals('bytes', with_sub=True),
BytesEncoder,
decoding.BytesDecoder,
label='bytes<M>',
)
_registry.register(
BaseEquals('bytes', with_sub=False),
ByteStringEncoder,
decoding.ByteStringDecoder,
label='bytes',
)
_registry.register(
BaseEquals('string'),
TextStringEncoder,
decoding.StringDecoder,
label='string',
)
registry.unregister('address')
registry.unregister('bytes<M>')
registry.unregister('bytes')
registry.unregister('string')
tron_patch_ethereum_types(registry)
_codec = ABICodec(registry)
encode_abi = _codec.encode_abi
encode_single = _codec.encode_single
decode_abi = _codec.decode_abi
decode_single = _codec.decode_single
is_encodable = _codec.is_encodable
is_encodable_type = _codec.is_encodable_type
Functions
def abi_data_tree(types='__no__default__', data='__no__default__') ‑> list
-
Decorate the data tree with pairs of (type, data). The pair tuple is actually an ABITypedData, but can be accessed as a tuple.
Examples
>>> abi_data_tree(types=["bool[2]", "uint"], data=[[True, False], 0])
Returns
[("bool[2]", [("bool", True), ("bool", False)]), ("uint256", 0)]
def abi_sub_tree(data_type, data_value)
-
Expand source code
def abi_sub_tree(data_type, data_value): if data_type is None: return ABITypedData([None, data_value]) try: base, sub, arrlist = data_type except ValueError: base, sub, arrlist = process_type(data_type) collapsed = collapse_type(base, sub, arrlist) if arrlist: sub_type = (base, sub, arrlist[:-1]) return ABITypedData([ collapsed, [ abi_sub_tree(sub_type, sub_value) for sub_value in data_value ], ]) else: return ABITypedData([collapsed, data_value])
def abi_to_signature(abi)
-
Expand source code
def abi_to_signature(abi): function_signature = "{fn_name}({fn_input_types})".format( fn_name=abi['name'], fn_input_types=','.join([ arg['type'] for arg in normalize_event_input_types(abi.get('inputs', [])) ]), ) return function_signature
def check_if_arguments_can_be_encoded(function_abi, args, kwargs)
-
Expand source code
def check_if_arguments_can_be_encoded(function_abi, args, kwargs): try: arguments = merge_args_and_kwargs(function_abi, args, kwargs) except TypeError: return False if len(function_abi.get('inputs', [])) != len(arguments): return False types = get_abi_input_types(function_abi) return all( is_encodable(_type, arg) for _type, arg in zip(types, arguments) )
def collapse_type(base, sub, arrlist)
-
Expand source code
def collapse_type(base, sub, arrlist): return base + str(sub) + ''.join(map(repr, arrlist))
def data_tree_map(func='__no__default__', data_tree='__no__default__')
-
Map func to every ABITypedData element in the tree. func will receive two args: abi_type, and data
def fallback_func_abi_exists(contract_abi) ‑> list
-
Expand source code
def fallback_func_abi_exists(contract_abi) -> list: return filter_by_type('fallback', contract_abi)
def filter_by_argument_count(num_arguments, contract_abi) ‑> list
-
Expand source code
def filter_by_argument_count(num_arguments, contract_abi) -> list: return [ abi for abi in contract_abi if len(abi['inputs']) == num_arguments ]
def filter_by_argument_name(argument_names, contract_abi)
-
Expand source code
def filter_by_argument_name(argument_names, contract_abi): return [ abi for abi in contract_abi if set(argument_names).intersection( get_abi_input_names(abi) ) == set(argument_names) ]
def filter_by_encodability(args, kwargs, contract_abi) ‑> list
-
Expand source code
def filter_by_encodability(args, kwargs, contract_abi) -> list: return [ function_abi for function_abi in contract_abi if check_if_arguments_can_be_encoded(function_abi, args, kwargs) ]
def filter_by_name(name, contract_abi) ‑> list
-
Expand source code
def filter_by_name(name, contract_abi) -> list: return [ abi for abi in contract_abi if ( abi['type'] not in ('fallback', 'constructor') and abi['name'] == name ) ]
def filter_by_type(_type, contract_abi) ‑> list
-
Expand source code
def filter_by_type(_type, contract_abi) -> list: return [abi for abi in contract_abi if abi['type'] == _type]
def get_abi_input_names(abi) ‑> list
-
Expand source code
def get_abi_input_names(abi) -> list: if 'inputs' not in abi and abi['type'] == 'fallback': return [] else: return [arg['name'] for arg in abi['inputs']]
def get_abi_input_types(abi) ‑> list
-
Expand source code
def get_abi_input_types(abi) -> list: if 'inputs' not in abi and abi['type'] == 'fallback': return [] else: return [arg['type'] for arg in abi['inputs']]
def get_abi_output_types(abi) ‑> list
-
Expand source code
def get_abi_output_types(abi) -> list: if abi['type'] == 'fallback': return [] else: return [arg['type'] for arg in abi['outputs']]
def get_constructor_abi(contract_abi)
-
Expand source code
def get_constructor_abi(contract_abi): candidates = [ abi for abi in contract_abi if abi['type'] == 'constructor' ] if len(candidates) == 1: return candidates[0] elif len(candidates) == 0: return None elif len(candidates) > 1: raise ValueError("Found multiple constructors.")
def get_fallback_func_abi(contract_abi)
-
Expand source code
def get_fallback_func_abi(contract_abi): fallback_abis = filter_by_type('fallback', contract_abi) if fallback_abis: return fallback_abis[0] else: raise FallbackNotFound("No fallback function was found in the contract ABI.")
def is_address_type(abi_type) ‑> bool
-
Expand source code
def is_address_type(abi_type) -> bool: return abi_type == 'address'
def is_array_type(abi_type) ‑> bool
-
Expand source code
def is_array_type(abi_type) -> bool: return bool(re.match(ARRAY_REGEX, abi_type))
def is_bool_type(abi_type) ‑> bool
-
Expand source code
def is_bool_type(abi_type) -> bool: return abi_type == 'bool'
def is_bytes_type(abi_type) ‑> bool
-
Expand source code
def is_bytes_type(abi_type) -> bool: return abi_type in BYTES_TYPES + ['bytes']
def is_int_type(abi_type) ‑> bool
-
Expand source code
def is_int_type(abi_type) -> bool: return abi_type in INT_TYPES
def is_length(target_length='__no__default__', value='__no__default__') ‑> bool
def is_probably_enum(abi_type)
-
Expand source code
def is_probably_enum(abi_type): return bool(re.match(ENUM_REGEX, abi_type))
def is_recognized_type(abi_type) ‑> bool
-
Expand source code
def is_recognized_type(abi_type) -> bool: return bool(re.match(TYPE_REGEX, abi_type))
def is_string_type(abi_type) ‑> bool
-
Expand source code
def is_string_type(abi_type) -> bool: return abi_type == 'string'
def is_uint_type(abi_type) ‑> bool
-
Expand source code
def is_uint_type(abi_type) -> bool: return abi_type in UINT_TYPES
def length_of_array_type(abi_type) ‑>
-
Expand source code
def length_of_array_type(abi_type) -> any: if not is_array_type(abi_type): raise ValueError( "Cannot parse length of nonarray abi-type: {0}".format(abi_type) ) inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip("[]") if not inner_brackets: return None else: return int(inner_brackets)
def map_abi_data(normalizers='__no__default__', types='__no__default__', data='__no__default__')
-
This function will apply normalizers to your data, in the context of the relevant types. Each normalizer is in the format:
def normalizer(datatype, data): # Conditionally modify data return (datatype, data)
Where datatype is a valid ABI type string, like "uint".
In case of an array, like "bool[2]", normalizer will receive
data
as an iterable of typed data, like[("bool", True), ("bool", False)]
.Internals
This is accomplished by:
- Decorating the data tree with types
- Recursively mapping each of the normalizers to the data
- Stripping the types back out of the tree
def merge_args_and_kwargs(function_abi, args, kwargs)
-
Expand source code
def merge_args_and_kwargs(function_abi, args, kwargs): if len(args) + len(kwargs) != len(function_abi.get('inputs', [])): raise TypeError( "Incorrect argument count. Expected '{0}'. Got '{1}'".format( len(function_abi['inputs']), len(args) + len(kwargs), ) ) if not kwargs: return args args_as_kwargs = { arg_abi['name']: arg for arg_abi, arg in zip(function_abi['inputs'], args) } duplicate_keys = set(args_as_kwargs).intersection(kwargs.keys()) if duplicate_keys: raise TypeError( "{fn_name}() got multiple values for argument(s) '{dups}'".format( fn_name=function_abi['name'], dups=', '.join(duplicate_keys), ) ) sorted_arg_names = [arg_abi['name'] for arg_abi in function_abi['inputs']] unknown_kwargs = {key for key in kwargs.keys() if key not in sorted_arg_names} if unknown_kwargs: if function_abi.get('name'): raise TypeError( "{fn_name}() got unexpected keyword argument(s) '{dups}'".format( fn_name=function_abi.get('name'), dups=', '.join(unknown_kwargs), ) ) # show type instead of name in the error message incase key 'name' is missing. raise TypeError( "Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format( _type=function_abi.get('type'), dups=', '.join(unknown_kwargs), ) ) sorted_args = list(zip( *sorted( itertools.chain(kwargs.items(), args_as_kwargs.items()), key=lambda kv: sorted_arg_names.index(kv[0]) ) )) if sorted_args: return sorted_args[1] else: return tuple()
def method_result_handler(r: dict) ‑> Tuple[bool, str, str]
-
Expand source code
def method_result_handler(r: dict) -> Tuple[bool, str, str]: if "result" in r["result"]: transID = r["transaction"]["txID"] resultcode = r["result"]["result"] if resultcode: if "constant_result" in r: return True, r["constant_result"], transID else: return True, "", transID else: print("======") print(r) print("======") return True, "", "" elif "code" in r["result"]: resultcode = r["result"]["code"] if RES_CODE.get(resultcode) > 0: return False, resultcode, r["result"]["message"] else: return True, resultcode, r["result"]["message"]
def normalize_event_input_types(abi_args)
-
Expand source code
@to_tuple def normalize_event_input_types(abi_args): for arg in abi_args: if is_recognized_type(arg['type']): yield arg elif is_probably_enum(arg['type']): yield {k: 'uint8' if k == 'type' else v for k, v in arg.items()} else: yield arg
def process_type(type_str)
-
Expand source code
def process_type(type_str): normalized_type_str = normalize_type_string(type_str) abi_type = parse_type_string(normalized_type_str) if isinstance(abi_type, TupleType): type_str_repr = repr(type_str) if type_str != normalized_type_str: type_str_repr = '{} (normalized to {})'.format( type_str_repr, repr(normalized_type_str), ) raise ValueError( "Cannot process type {}: tuple types not supported".format( type_str_repr, ) ) abi_type.validate() sub = abi_type.sub if isinstance(sub, tuple): sub = 'x'.join(map(str, sub)) elif isinstance(sub, int): sub = str(sub) else: sub = '' arrlist = abi_type.arrlist if isinstance(arrlist, tuple): arrlist = list(map(list, arrlist)) else: arrlist = [] return abi_type.base, sub, arrlist
def size_of_type(abi_type)
-
Returns size in bits of abi_type
Expand source code
def size_of_type(abi_type): """ Returns size in bits of abi_type """ if 'string' in abi_type: return None if 'byte' in abi_type: return None if '[' in abi_type: return None if abi_type == 'bool': return 8 if abi_type == 'address': return 160 return int(re.sub(r"\D", "", abi_type))
def strip_abi_type(elements)
-
Expand source code
def strip_abi_type(elements): if isinstance(elements, ABITypedData): return elements.data else: return elements
def sub_type_of_array_type(abi_type)
-
Expand source code
def sub_type_of_array_type(abi_type): if not is_array_type(abi_type): raise ValueError( "Cannot parse subtype of nonarray abi-type: {0}".format(abi_type) ) return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1)
def tron_patch_ethereum_types(_registry: eth_abi.registry.ABIRegistry)
-
Expand source code
def tron_patch_ethereum_types(_registry: ABIRegistry): _registry.register( BaseEquals('address'), TronAddressEncoder, TronAddressDecoder, label='address', ) _registry.register( BaseEquals('trcToken'), eth_abi.encoding.UnsignedIntegerEncoder, eth_abi.decoding.UnsignedIntegerDecoder, label='trcToken', ) _registry.register( BaseEquals('trc20'), eth_abi.encoding.UnsignedIntegerEncoder, eth_abi.decoding.UnsignedIntegerDecoder, label='trc20', ) _registry.register( BaseEquals('bytes', with_sub=True), BytesEncoder, decoding.BytesDecoder, label='bytes<M>', ) _registry.register( BaseEquals('bytes', with_sub=False), ByteStringEncoder, decoding.ByteStringDecoder, label='bytes', ) _registry.register( BaseEquals('string'), TextStringEncoder, decoding.StringDecoder, label='string', )
Classes
class ABITypedData (iterable)
-
This class marks data as having a certain ABI-type.
>>> a1 = ABITypedData(['address', addr1]) >>> a2 = ABITypedData(['address', addr2]) >>> addrs = ABITypedData(['address[]', [a1, a2])
You can access the fields using tuple() interface, or with attributes:
>>> assert a1.abi_type == a1[0] >>> assert a1.data == a1[1]
Unlike a typical
namedtuple
, you initialize with a single positional argument that is iterable, to match the init interface of all other relevant collections.Expand source code
class ABITypedData(namedtuple('ABITypedData', 'abi_type, data')): """ This class marks data as having a certain ABI-type. >>> a1 = ABITypedData(['address', addr1]) >>> a2 = ABITypedData(['address', addr2]) >>> addrs = ABITypedData(['address[]', [a1, a2]) You can access the fields using tuple() interface, or with attributes: >>> assert a1.abi_type == a1[0] >>> assert a1.data == a1[1] Unlike a typical `namedtuple`, you initialize with a single positional argument that is iterable, to match the init interface of all other relevant collections. """ def __new__(cls, iterable): return super().__new__(cls, *iterable)
Ancestors
- builtins.tuple
class AcceptsHexStrMixin
-
Expand source code
class AcceptsHexStrMixin: def validate_value(self, value): if is_text(value): try: value = decode_hex(value) except binascii.Error: self.invalidate_value( value, msg='invalid hex string', ) super().validate_value(value)
Subclasses
Methods
def validate_value(self, value)
-
Expand source code
def validate_value(self, value): if is_text(value): try: value = decode_hex(value) except binascii.Error: self.invalidate_value( value, msg='invalid hex string', ) super().validate_value(value)
class ByteStringEncoder (**kwargs)
-
Base class for all encoder classes. Subclass this if you want to define a custom encoder class. Subclasses must also implement :any:
BaseCoder.from_type_str
.Expand source code
class ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder): pass
Ancestors
- AcceptsHexStrMixin
- eth_abi.encoding.ByteStringEncoder
- eth_abi.encoding.BaseEncoder
- eth_abi.base.BaseCoder
class BytesEncoder (**kwargs)
-
Base class for all encoder classes. Subclass this if you want to define a custom encoder class. Subclasses must also implement :any:
BaseCoder.from_type_str
.Expand source code
class BytesEncoder(AcceptsHexStrMixin, encoding.BytesEncoder): pass
Ancestors
- AcceptsHexStrMixin
- eth_abi.encoding.BytesEncoder
- eth_abi.encoding.Fixed32ByteSizeEncoder
- eth_abi.encoding.FixedSizeEncoder
- eth_abi.encoding.BaseEncoder
- eth_abi.base.BaseCoder
class TextStringEncoder (**kwargs)
-
Base class for all encoder classes. Subclass this if you want to define a custom encoder class. Subclasses must also implement :any:
BaseCoder.from_type_str
.Expand source code
class TextStringEncoder(encoding.TextStringEncoder): @classmethod def validate_value(cls, value): if is_bytes(value): try: value = to_text(value) except UnicodeDecodeError: cls.invalidate_value( value, msg='not decodable as unicode string', ) super().validate_value(value)
Ancestors
- eth_abi.encoding.TextStringEncoder
- eth_abi.encoding.BaseEncoder
- eth_abi.base.BaseCoder
Static methods
def validate_value(value)
-
Checks whether or not the given value can be encoded by this encoder. If the given value cannot be encoded, must raise :any:
exceptions.EncodingError
.Expand source code
@classmethod def validate_value(cls, value): if is_bytes(value): try: value = to_text(value) except UnicodeDecodeError: cls.invalidate_value( value, msg='not decodable as unicode string', ) super().validate_value(value)
class TronAddressDecoder (**kwargs)
-
Base class for all decoder classes. Subclass this if you want to define a custom decoder class. Subclasses must also implement :any:
BaseCoder.from_type_str
.Expand source code
class TronAddressDecoder(Fixed32ByteSizeDecoder): value_bit_size = 20 * 8 is_big_endian = True decoder_fn = staticmethod(to_base58check_address) @parse_type_str("address") def from_type_str(cls, abi_type, registry): return cls()
Ancestors
- eth_abi.decoding.Fixed32ByteSizeDecoder
- eth_abi.decoding.FixedByteSizeDecoder
- eth_abi.decoding.SingleDecoder
- eth_abi.decoding.BaseDecoder
- eth_abi.base.BaseCoder
Class variables
var is_big_endian
var value_bit_size
Static methods
def decoder_fn(raw_addr: Union[str, bytes]) ‑> str
-
Convert hex address or base58check address to base58check address(and verify it).
Expand source code
def to_base58check_address(raw_addr: Union[str, bytes]) -> str: """Convert hex address or base58check address to base58check address(and verify it).""" if isinstance(raw_addr, (str,)): if raw_addr[0] == "T" and len(raw_addr) == 34: try: # assert checked base58.b58decode_check(raw_addr) except ValueError: raise BadAddress("bad base58check format") return raw_addr elif len(raw_addr) == 42: if raw_addr.startswith("0x"): # eth address format return base58.b58encode_check(b"\x41" + bytes.fromhex(raw_addr[2:])).decode() else: return base58.b58encode_check(bytes.fromhex(raw_addr)).decode() elif raw_addr.startswith("0x") and len(raw_addr) == 44: return base58.b58encode_check(bytes.fromhex(raw_addr[2:])).decode() elif isinstance(raw_addr, (bytes, bytearray)): if len(raw_addr) == 21 and int(raw_addr[0]) == 0x41: return base58.b58encode_check(raw_addr).decode() if len(raw_addr) == 20: # eth address format return base58.b58encode_check(b"\x41" + raw_addr).decode() return to_base58check_address(raw_addr.decode()) raise BadAddress(repr(raw_addr))
def from_type_str(cls, abi_type, registry)
-
Expand source code
@parse_type_str("address") def from_type_str(cls, abi_type, registry): return cls()
class TronAddressEncoder (**kwargs)
-
Base class for all encoder classes. Subclass this if you want to define a custom encoder class. Subclasses must also implement :any:
BaseCoder.from_type_str
.Expand source code
class TronAddressEncoder(Fixed32ByteSizeEncoder): value_bit_size = 20 * 8 encode_fn = staticmethod(to_tvm_address) is_big_endian = True @classmethod def validate_value(cls, value): if not is_address(value): cls.invalidate_value(value) def validate(self): super().validate() if self.value_bit_size != 20 * 8: raise ValueError("Addresses must be 160 bits in length") @parse_type_str("address") def from_type_str(cls, abi_type, registry): return cls()
Ancestors
- eth_abi.encoding.Fixed32ByteSizeEncoder
- eth_abi.encoding.FixedSizeEncoder
- eth_abi.encoding.BaseEncoder
- eth_abi.base.BaseCoder
Class variables
var is_big_endian
var value_bit_size
Static methods
def encode_fn(raw_addr: Union[str, bytes]) ‑> bytes
-
Expand source code
def to_tvm_address(raw_addr: Union[str, bytes]) -> bytes: return to_raw_address(raw_addr)[1:]
def from_type_str(cls, abi_type, registry)
-
Expand source code
@parse_type_str("address") def from_type_str(cls, abi_type, registry): return cls()
def validate_value(value)
-
Checks whether or not the given value can be encoded by this encoder. If the given value cannot be encoded, must raise :any:
exceptions.EncodingError
.Expand source code
@classmethod def validate_value(cls, value): if not is_address(value): cls.invalidate_value(value)
Methods
def validate(self)
-
Expand source code
def validate(self): super().validate() if self.value_bit_size != 20 * 8: raise ValueError("Addresses must be 160 bits in length")