diff --git a/crypto/utils/abi/argument_decoder.py b/crypto/utils/abi/argument_decoder.py new file mode 100644 index 00000000..eecab9c9 --- /dev/null +++ b/crypto/utils/abi/argument_decoder.py @@ -0,0 +1,33 @@ +import binascii +from crypto.utils.abi_decoder import AbiDecoder + +class ArgumentDecoder: + def __init__(self, hex_string: str): + try: + self.bytes = binascii.unhexlify(hex_string) + except binascii.Error: + self.bytes = b'' + + def decode_string(self) -> str: + value, _ = AbiDecoder.decode_string(self.bytes, 0) + + return value + + def decode_address(self) -> str: + value, _ = AbiDecoder.decode_address(self.bytes, 0) + + return value + + def decode_unsigned_int(self) -> int: + value, _ = AbiDecoder.decode_number(self.bytes, 0, False) + + return value + + def decode_signed_int(self) -> int: + value, _ = AbiDecoder.decode_number(self.bytes, 0, True) + + return value + + def decode_bool(self) -> bool: + value, _ = AbiDecoder.decode_bool(self.bytes, 0) + return value diff --git a/crypto/utils/abi_decoder.py b/crypto/utils/abi_decoder.py index 71082071..53466a4d 100644 --- a/crypto/utils/abi_decoder.py +++ b/crypto/utils/abi_decoder.py @@ -64,32 +64,36 @@ def decode_parameter(self, bytes_data, offset, param): if match: signed = match.group(1) == 'int' bits = int(match.group(2)) - return self.decode_number(bytes_data, offset, bits, signed) + return self.decode_number(bytes_data, offset, signed) if type_ == 'tuple': return self.decode_tuple(bytes_data, offset, param) raise Exception('Unsupported type: ' + type_) - def decode_address(self, bytes_data, offset): + @staticmethod + def decode_address(bytes_data, offset): data = bytes_data[offset:offset+32] address_bytes = data[12:32] address = '0x' + address_bytes.hex() address = get_checksum_address(address) return address, 32 - def decode_bool(self, bytes_data, offset): + @staticmethod + def decode_bool(bytes_data, offset): data = bytes_data[offset:offset+32] value = int.from_bytes(data, byteorder='big') != 0 return value, 32 - def decode_number(self, bytes_data, offset, bits, signed): + @staticmethod + def decode_number(bytes_data, offset, signed): data = bytes_data[offset:offset+32] value = int.from_bytes(data, byteorder='big', signed=signed) return value, 32 - def decode_string(self, bytes_data, offset): - data_offset = self.read_uint(bytes_data, offset) + @classmethod + def decode_string(cls, bytes_data, offset): + data_offset = cls.read_uint(bytes_data, offset) string_offset = offset + data_offset - length = self.read_uint(bytes_data, string_offset) + length = cls.read_uint(bytes_data, string_offset) string_data = bytes_data[string_offset+32:string_offset+32+length] value = string_data.decode('utf-8') return value, 32 @@ -140,6 +144,7 @@ def decode_tuple(self, bytes_data, offset, param): values[name] = value return values, 32 - def read_uint(self, bytes_data, offset): + @staticmethod + def read_uint(bytes_data, offset): data = bytes_data[offset:offset+32] return int.from_bytes(data, byteorder='big') diff --git a/tests/utils/abi/test_argument_decoder.py b/tests/utils/abi/test_argument_decoder.py new file mode 100644 index 00000000..4feacbdc --- /dev/null +++ b/tests/utils/abi/test_argument_decoder.py @@ -0,0 +1,42 @@ +from crypto.utils.abi.argument_decoder import ArgumentDecoder + + +def test_it_should_decode_address(): + payload = '000000000000000000000000512F366D524157BcF734546eB29a6d687B762255' + expected = '0x512F366D524157BcF734546eB29a6d687B762255' + + decoder = ArgumentDecoder(payload) + + assert decoder.decode_address() == expected + +def test_it_should_decode_unsigned_int(): + payload = '000000000000000000000000000000000000000000000000016345785d8a0000' + expected = 100000000000000000 + + decoder = ArgumentDecoder(payload) + + assert decoder.decode_unsigned_int() == expected + +def test_it_should_decode_signed_int(): + payload = '000000000000000000000000000000000000000000000000016345785d8a0000' + expected = 100000000000000000 + + decoder = ArgumentDecoder(payload) + + assert decoder.decode_signed_int() == expected + +def test_it_should_decode_bool_as_true(): + payload = '0000000000000000000000000000000000000000000000000000000000000001' + expected = True + + decoder = ArgumentDecoder(payload) + + assert decoder.decode_bool() == expected + +def test_it_should_decode_bool_as_false(): + payload = '0000000000000000000000000000000000000000000000000000000000000000' + expected = False + + decoder = ArgumentDecoder(payload) + + assert decoder.decode_bool() == expected