github.com/benorgera/go-ethereum@v1.10.18-0.20220401011646-b3f57b1a73ba/accounts/abi/selector_parser.go (about)

     1  package abi
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  type SelectorMarshaling struct {
     8  	Name   string               `json:"name"`
     9  	Type   string               `json:"type"`
    10  	Inputs []ArgumentMarshaling `json:"inputs"`
    11  }
    12  
    13  func isDigit(c byte) bool {
    14  	return c >= '0' && c <= '9'
    15  }
    16  
    17  func isAlpha(c byte) bool {
    18  	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
    19  }
    20  
    21  func isIdentifierSymbol(c byte) bool {
    22  	return c == '$' || c == '_'
    23  }
    24  
    25  func parseToken(unescapedSelector string, isIdent bool) (string, string, error) {
    26  	if len(unescapedSelector) == 0 {
    27  		return "", "", fmt.Errorf("empty token")
    28  	}
    29  	firstChar := unescapedSelector[0]
    30  	position := 1
    31  	if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
    32  		return "", "", fmt.Errorf("invalid token start: %c", firstChar)
    33  	}
    34  	for position < len(unescapedSelector) {
    35  		char := unescapedSelector[position]
    36  		if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
    37  			break
    38  		}
    39  		position++
    40  	}
    41  	return unescapedSelector[:position], unescapedSelector[position:], nil
    42  }
    43  
    44  func parseIdentifier(unescapedSelector string) (string, string, error) {
    45  	return parseToken(unescapedSelector, true)
    46  }
    47  
    48  func parseElementaryType(unescapedSelector string) (string, string, error) {
    49  	parsedType, rest, err := parseToken(unescapedSelector, false)
    50  	if err != nil {
    51  		return "", "", fmt.Errorf("failed to parse elementary type: %v", err)
    52  	}
    53  	// handle arrays
    54  	for len(rest) > 0 && rest[0] == '[' {
    55  		parsedType = parsedType + string(rest[0])
    56  		rest = rest[1:]
    57  		for len(rest) > 0 && isDigit(rest[0]) {
    58  			parsedType = parsedType + string(rest[0])
    59  			rest = rest[1:]
    60  		}
    61  		if len(rest) == 0 || rest[0] != ']' {
    62  			return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
    63  		}
    64  		parsedType = parsedType + string(rest[0])
    65  		rest = rest[1:]
    66  	}
    67  	return parsedType, rest, nil
    68  }
    69  
    70  func parseCompositeType(unescapedSelector string) ([]interface{}, string, error) {
    71  	if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
    72  		return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
    73  	}
    74  	parsedType, rest, err := parseType(unescapedSelector[1:])
    75  	if err != nil {
    76  		return nil, "", fmt.Errorf("failed to parse type: %v", err)
    77  	}
    78  	result := []interface{}{parsedType}
    79  	for len(rest) > 0 && rest[0] != ')' {
    80  		parsedType, rest, err = parseType(rest[1:])
    81  		if err != nil {
    82  			return nil, "", fmt.Errorf("failed to parse type: %v", err)
    83  		}
    84  		result = append(result, parsedType)
    85  	}
    86  	if len(rest) == 0 || rest[0] != ')' {
    87  		return nil, "", fmt.Errorf("expected ')', got '%s'", rest)
    88  	}
    89  	return result, rest[1:], nil
    90  }
    91  
    92  func parseType(unescapedSelector string) (interface{}, string, error) {
    93  	if len(unescapedSelector) == 0 {
    94  		return nil, "", fmt.Errorf("empty type")
    95  	}
    96  	if unescapedSelector[0] == '(' {
    97  		return parseCompositeType(unescapedSelector)
    98  	} else {
    99  		return parseElementaryType(unescapedSelector)
   100  	}
   101  }
   102  
   103  func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
   104  	arguments := make([]ArgumentMarshaling, 0)
   105  	for i, arg := range args {
   106  		// generate dummy name to avoid unmarshal issues
   107  		name := fmt.Sprintf("name%d", i)
   108  		if s, ok := arg.(string); ok {
   109  			arguments = append(arguments, ArgumentMarshaling{name, s, s, nil, false})
   110  		} else if components, ok := arg.([]interface{}); ok {
   111  			subArgs, err := assembleArgs(components)
   112  			if err != nil {
   113  				return nil, fmt.Errorf("failed to assemble components: %v", err)
   114  			}
   115  			arguments = append(arguments, ArgumentMarshaling{name, "tuple", "tuple", subArgs, false})
   116  		} else {
   117  			return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
   118  		}
   119  	}
   120  	return arguments, nil
   121  }
   122  
   123  // ParseSelector converts a method selector into a struct that can be JSON encoded
   124  // and consumed by other functions in this package.
   125  // Note, although uppercase letters are not part of the ABI spec, this function
   126  // still accepts it as the general format is valid.
   127  func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
   128  	name, rest, err := parseIdentifier(unescapedSelector)
   129  	if err != nil {
   130  		return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
   131  	}
   132  	args := []interface{}{}
   133  	if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
   134  		rest = rest[2:]
   135  	} else {
   136  		args, rest, err = parseCompositeType(rest)
   137  		if err != nil {
   138  			return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
   139  		}
   140  	}
   141  	if len(rest) > 0 {
   142  		return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
   143  	}
   144  
   145  	// Reassemble the fake ABI and constuct the JSON
   146  	fakeArgs, err := assembleArgs(args)
   147  	if err != nil {
   148  		return SelectorMarshaling{}, fmt.Errorf("failed to parse selector: %v", err)
   149  	}
   150  
   151  	return SelectorMarshaling{name, "function", fakeArgs}, nil
   152  }