add signature parser

This commit is contained in:
banteg 2020-01-28 15:49:15 +07:00
parent 4ffba7ff20
commit 5c9a6c298a
3 changed files with 66 additions and 0 deletions

39
multicall/signature.py Normal file
View File

@ -0,0 +1,39 @@
from eth_abi import encode_single, decode_single
from eth_utils import function_signature_to_4byte_selector
def parse_signature(signature):
"""
Breaks 'func(address)(uint256)' into ['func', '(address)', '(uint256)']
"""
parts = []
stack = []
start = 0
for end, letter in enumerate(signature):
if letter == '(':
stack.append(letter)
if not parts:
parts.append(signature[start:end])
start = end
if letter == ')':
stack.pop()
if not stack: # we are only interested in outermost groups
parts.append(signature[start:end + 1])
start = end + 1
return parts
class Signature:
def __init__(self, signature):
self.signature = signature
self.parts = parse_signature(signature)
self.input_types = self.parts[1]
self.output_types = self.parts[2]
self.function = ''.join(self.parts[:2])
self.fourbyte = function_signature_to_4byte_selector(self.function)
def encode_data(self, args=None):
return self.fourbyte + encode_single(self.input_types, args) if args else self.fourbyte
def decode_data(self, output):
return decode_single(self.output_types, output)

4
tests/conftest.py Normal file
View File

@ -0,0 +1,4 @@
import os
import sys
sys.path.insert(0, os.path.abspath('.'))

23
tests/test_signature.py Normal file
View File

@ -0,0 +1,23 @@
from eth_abi import encode_abi, decode_abi
from multicall import signature
args = ((1, 2, 3), '0x' + 'f' * 40, b'data')
types = ['uint256[]', 'address', 'bytes']
def test_signature_parsing():
sig = signature.Signature('aggregate((address,bytes)[])(uint256,bytes[])')
assert sig.parts == ['aggregate', '((address,bytes)[])', '(uint256,bytes[])']
assert sig.input_types == '((address,bytes)[])'
assert sig.output_types == '(uint256,bytes[])'
def test_signature_encoding():
sig = signature.Signature('test(uint256[],address,bytes)()')
assert sig.encode_data(args) == sig.fourbyte + encode_abi(types, args)
def test_signature_decoding():
sig = signature.Signature('test()(uint256[],address,bytes)')
data = encode_abi(types, args)
assert sig.decode_data(data) == args