github.com/jimmyx0x/go-ethereum@v1.10.28/accounts/abi/selector_parser.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package abi 18 19 import ( 20 "fmt" 21 ) 22 23 type SelectorMarshaling struct { 24 Name string `json:"name"` 25 Type string `json:"type"` 26 Inputs []ArgumentMarshaling `json:"inputs"` 27 } 28 29 func isDigit(c byte) bool { 30 return c >= '0' && c <= '9' 31 } 32 33 func isAlpha(c byte) bool { 34 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 35 } 36 37 func isIdentifierSymbol(c byte) bool { 38 return c == '$' || c == '_' 39 } 40 41 func parseToken(unescapedSelector string, isIdent bool) (string, string, error) { 42 if len(unescapedSelector) == 0 { 43 return "", "", fmt.Errorf("empty token") 44 } 45 firstChar := unescapedSelector[0] 46 position := 1 47 if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) { 48 return "", "", fmt.Errorf("invalid token start: %c", firstChar) 49 } 50 for position < len(unescapedSelector) { 51 char := unescapedSelector[position] 52 if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) { 53 break 54 } 55 position++ 56 } 57 return unescapedSelector[:position], unescapedSelector[position:], nil 58 } 59 60 func parseIdentifier(unescapedSelector string) (string, string, error) { 61 return parseToken(unescapedSelector, true) 62 } 63 64 func parseElementaryType(unescapedSelector string) (string, string, error) { 65 parsedType, rest, err := parseToken(unescapedSelector, false) 66 if err != nil { 67 return "", "", fmt.Errorf("failed to parse elementary type: %v", err) 68 } 69 // handle arrays 70 for len(rest) > 0 && rest[0] == '[' { 71 parsedType = parsedType + string(rest[0]) 72 rest = rest[1:] 73 for len(rest) > 0 && isDigit(rest[0]) { 74 parsedType = parsedType + string(rest[0]) 75 rest = rest[1:] 76 } 77 if len(rest) == 0 || rest[0] != ']' { 78 return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0]) 79 } 80 parsedType = parsedType + string(rest[0]) 81 rest = rest[1:] 82 } 83 return parsedType, rest, nil 84 } 85 86 func parseCompositeType(unescapedSelector string) ([]interface{}, string, error) { 87 if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' { 88 return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0]) 89 } 90 parsedType, rest, err := parseType(unescapedSelector[1:]) 91 if err != nil { 92 return nil, "", fmt.Errorf("failed to parse type: %v", err) 93 } 94 result := []interface{}{parsedType} 95 for len(rest) > 0 && rest[0] != ')' { 96 parsedType, rest, err = parseType(rest[1:]) 97 if err != nil { 98 return nil, "", fmt.Errorf("failed to parse type: %v", err) 99 } 100 result = append(result, parsedType) 101 } 102 if len(rest) == 0 || rest[0] != ')' { 103 return nil, "", fmt.Errorf("expected ')', got '%s'", rest) 104 } 105 if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' { 106 return append(result, "[]"), rest[3:], nil 107 } 108 return result, rest[1:], nil 109 } 110 111 func parseType(unescapedSelector string) (interface{}, string, error) { 112 if len(unescapedSelector) == 0 { 113 return nil, "", fmt.Errorf("empty type") 114 } 115 if unescapedSelector[0] == '(' { 116 return parseCompositeType(unescapedSelector) 117 } else { 118 return parseElementaryType(unescapedSelector) 119 } 120 } 121 122 func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) { 123 arguments := make([]ArgumentMarshaling, 0) 124 for i, arg := range args { 125 // generate dummy name to avoid unmarshal issues 126 name := fmt.Sprintf("name%d", i) 127 if s, ok := arg.(string); ok { 128 arguments = append(arguments, ArgumentMarshaling{name, s, s, nil, false}) 129 } else if components, ok := arg.([]interface{}); ok { 130 subArgs, err := assembleArgs(components) 131 if err != nil { 132 return nil, fmt.Errorf("failed to assemble components: %v", err) 133 } 134 tupleType := "tuple" 135 if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" { 136 subArgs = subArgs[:len(subArgs)-1] 137 tupleType = "tuple[]" 138 } 139 arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false}) 140 } else { 141 return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg) 142 } 143 } 144 return arguments, nil 145 } 146 147 // ParseSelector converts a method selector into a struct that can be JSON encoded 148 // and consumed by other functions in this package. 149 // Note, although uppercase letters are not part of the ABI spec, this function 150 // still accepts it as the general format is valid. 151 func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) { 152 name, rest, err := parseIdentifier(unescapedSelector) 153 if err != nil { 154 return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err) 155 } 156 args := []interface{}{} 157 if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' { 158 rest = rest[2:] 159 } else { 160 args, rest, err = parseCompositeType(rest) 161 if err != nil { 162 return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err) 163 } 164 } 165 if len(rest) > 0 { 166 return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest) 167 } 168 169 // Reassemble the fake ABI and construct the JSON 170 fakeArgs, err := assembleArgs(args) 171 if err != nil { 172 return SelectorMarshaling{}, fmt.Errorf("failed to parse selector: %v", err) 173 } 174 175 return SelectorMarshaling{name, "function", fakeArgs}, nil 176 }