mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-18 22:09:26 +00:00
accounts/abi: support constructor/fallback/receive
This commit is contained in:
parent
bf541d1dfd
commit
fd81bb1b9f
2 changed files with 235 additions and 2 deletions
|
|
@ -68,9 +68,14 @@ func parseHumanReadableABIArray(signatures []string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
isEvent := resultType == "event"
|
isEvent := resultType == "event"
|
||||||
isFunction := resultType == "function"
|
isFunction := resultType == "function"
|
||||||
|
isConstructor := resultType == "constructor"
|
||||||
|
isFallback := resultType == "fallback"
|
||||||
|
isReceive := resultType == "receive"
|
||||||
|
|
||||||
if name, ok := result["name"]; ok {
|
if !isConstructor && !isFallback && !isReceive {
|
||||||
normalized["name"] = name
|
if name, ok := result["name"]; ok {
|
||||||
|
normalized["name"] = name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if inputs, ok := result["inputs"].([]ArgumentMarshaling); ok {
|
if inputs, ok := result["inputs"].([]ArgumentMarshaling); ok {
|
||||||
normInputs := make([]map[string]interface{}, len(inputs))
|
normInputs := make([]map[string]interface{}, len(inputs))
|
||||||
|
|
@ -169,6 +174,53 @@ func TestParseHumanReadableABI(t *testing.T) {
|
||||||
}
|
}
|
||||||
]`,
|
]`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "constructor",
|
||||||
|
input: []string{"constructor(address owner, uint256 initialSupply)"},
|
||||||
|
expected: `[
|
||||||
|
{
|
||||||
|
"type": "constructor",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "owner", "type": "address"},
|
||||||
|
{"name": "initialSupply", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constructor payable",
|
||||||
|
input: []string{"constructor(address owner) payable"},
|
||||||
|
expected: `[
|
||||||
|
{
|
||||||
|
"type": "constructor",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "owner", "type": "address"}
|
||||||
|
],
|
||||||
|
"stateMutability": "payable"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fallback function",
|
||||||
|
input: []string{"fallback()"},
|
||||||
|
expected: `[
|
||||||
|
{
|
||||||
|
"type": "fallback",
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "receive function",
|
||||||
|
input: []string{"receive() payable"},
|
||||||
|
expected: `[
|
||||||
|
{
|
||||||
|
"type": "receive",
|
||||||
|
"stateMutability": "payable"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "multiple functions",
|
name: "multiple functions",
|
||||||
input: []string{
|
input: []string{
|
||||||
|
|
@ -384,6 +436,48 @@ func TestParseHumanReadableABI(t *testing.T) {
|
||||||
}
|
}
|
||||||
]`,
|
]`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "int and uint without explicit sizes normalize to 256 bits",
|
||||||
|
input: []string{
|
||||||
|
"function testIntUint(int value1, uint value2)",
|
||||||
|
"function testArrays(int[] values1, uint[10] values2)",
|
||||||
|
"function testMixed(int value1, uint value2, int8 value3, uint256 value4)",
|
||||||
|
},
|
||||||
|
expected: `[
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "testIntUint",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "value1", "type": "int256"},
|
||||||
|
{"name": "value2", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "testArrays",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "values1", "type": "int256[]"},
|
||||||
|
{"name": "values2", "type": "uint256[10]"}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "testMixed",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "value1", "type": "int256"},
|
||||||
|
{"name": "value2", "type": "uint256"},
|
||||||
|
{"name": "value3", "type": "int8"},
|
||||||
|
{"name": "value4", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,18 @@ type ABIMarshaling map[string]interface{}
|
||||||
func ParseHumanReadableABI(signature string) (ABIMarshaling, error) {
|
func ParseHumanReadableABI(signature string) (ABIMarshaling, error) {
|
||||||
signature = skipWhitespace(signature)
|
signature = skipWhitespace(signature)
|
||||||
|
|
||||||
|
if strings.HasPrefix(signature, "constructor") {
|
||||||
|
return ParseConstructor(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(signature, "fallback") {
|
||||||
|
return ParseFallback(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(signature, "receive") {
|
||||||
|
return ParseReceive(signature)
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(signature, "event ") || (strings.Contains(signature, "(") && strings.Contains(signature, "indexed")) {
|
if strings.HasPrefix(signature, "event ") || (strings.Contains(signature, "(") && strings.Contains(signature, "indexed")) {
|
||||||
event, err := ParseEvent(signature)
|
event, err := ParseEvent(signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -151,6 +163,23 @@ func parseKeyword(s string, keyword string) (string, bool) {
|
||||||
return skipWhitespace(rest), true
|
return skipWhitespace(rest), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeType normalizes bare int/uint to int256/uint256, including arrays
|
||||||
|
func normalizeType(typeName string) string {
|
||||||
|
if typeName == "int" {
|
||||||
|
return "int256"
|
||||||
|
}
|
||||||
|
if typeName == "uint" {
|
||||||
|
return "uint256"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(typeName, "int[") {
|
||||||
|
return "int256" + typeName[3:]
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(typeName, "uint[") {
|
||||||
|
return "uint256" + typeName[4:]
|
||||||
|
}
|
||||||
|
return typeName
|
||||||
|
}
|
||||||
|
|
||||||
func parseElementaryType(unescapedSelector string) (string, string, error) {
|
func parseElementaryType(unescapedSelector string) (string, string, error) {
|
||||||
parsedType, rest, err := parseToken(unescapedSelector, false)
|
parsedType, rest, err := parseToken(unescapedSelector, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -169,6 +198,7 @@ func parseElementaryType(unescapedSelector string) (string, string, error) {
|
||||||
parsedType = parsedType + string(rest[0])
|
parsedType = parsedType + string(rest[0])
|
||||||
rest = rest[1:]
|
rest = rest[1:]
|
||||||
}
|
}
|
||||||
|
parsedType = normalizeType(parsedType)
|
||||||
return parsedType, rest, nil
|
return parsedType, rest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,6 +461,115 @@ func ParseError(unescapedSelector string) (ErrorMarshaling, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseConstructor parses a constructor signature
|
||||||
|
func ParseConstructor(signature string) (ABIMarshaling, error) {
|
||||||
|
signature = skipWhitespace(signature)
|
||||||
|
|
||||||
|
rest, _ := parseKeyword(signature, "constructor")
|
||||||
|
rest = skipWhitespace(rest)
|
||||||
|
|
||||||
|
if len(rest) == 0 || rest[0] != '(' {
|
||||||
|
return nil, fmt.Errorf("expected '(' after constructor keyword")
|
||||||
|
}
|
||||||
|
rest = rest[1:]
|
||||||
|
|
||||||
|
parenCount := 1
|
||||||
|
paramEnd := 0
|
||||||
|
for i := 0; i < len(rest); i++ {
|
||||||
|
if rest[i] == '(' {
|
||||||
|
parenCount++
|
||||||
|
} else if rest[i] == ')' {
|
||||||
|
parenCount--
|
||||||
|
if parenCount == 0 {
|
||||||
|
paramEnd = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parenCount != 0 {
|
||||||
|
return nil, fmt.Errorf("unbalanced parentheses in constructor signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
paramsStr := rest[:paramEnd]
|
||||||
|
arguments, err := parseParameterList(paramsStr, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse constructor parameters: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rest = skipWhitespace(rest[paramEnd+1:])
|
||||||
|
|
||||||
|
stateMutability := "nonpayable"
|
||||||
|
if newRest, found := parseKeyword(rest, "payable"); found {
|
||||||
|
stateMutability = "payable"
|
||||||
|
rest = newRest
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(ABIMarshaling)
|
||||||
|
result["type"] = "constructor"
|
||||||
|
result["inputs"] = arguments
|
||||||
|
result["stateMutability"] = stateMutability
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFallback parses a fallback function signature
|
||||||
|
// ParseFallback parses a fallback function signature (no parameters allowed)
|
||||||
|
func ParseFallback(signature string) (ABIMarshaling, error) {
|
||||||
|
signature = skipWhitespace(signature)
|
||||||
|
|
||||||
|
rest, _ := parseKeyword(signature, "fallback")
|
||||||
|
rest = skipWhitespace(rest)
|
||||||
|
|
||||||
|
if len(rest) == 0 || rest[0] != '(' {
|
||||||
|
return nil, fmt.Errorf("expected '(' after fallback keyword")
|
||||||
|
}
|
||||||
|
rest = rest[1:]
|
||||||
|
|
||||||
|
if len(rest) == 0 || rest[0] != ')' {
|
||||||
|
return nil, fmt.Errorf("fallback function cannot have parameters")
|
||||||
|
}
|
||||||
|
rest = skipWhitespace(rest[1:])
|
||||||
|
|
||||||
|
stateMutability := "nonpayable"
|
||||||
|
if newRest, found := parseKeyword(rest, "payable"); found {
|
||||||
|
stateMutability = "payable"
|
||||||
|
rest = newRest
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(ABIMarshaling)
|
||||||
|
result["type"] = "fallback"
|
||||||
|
result["stateMutability"] = stateMutability
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseReceive parses a receive function signature (no parameters, always payable)
|
||||||
|
func ParseReceive(signature string) (ABIMarshaling, error) {
|
||||||
|
signature = skipWhitespace(signature)
|
||||||
|
|
||||||
|
rest, _ := parseKeyword(signature, "receive")
|
||||||
|
rest = skipWhitespace(rest)
|
||||||
|
|
||||||
|
if len(rest) == 0 || rest[0] != '(' {
|
||||||
|
return nil, fmt.Errorf("expected '(' after receive keyword")
|
||||||
|
}
|
||||||
|
rest = rest[1:]
|
||||||
|
|
||||||
|
if len(rest) == 0 || rest[0] != ')' {
|
||||||
|
return nil, fmt.Errorf("receive function cannot have parameters")
|
||||||
|
}
|
||||||
|
rest = skipWhitespace(rest[1:])
|
||||||
|
|
||||||
|
stateMutability := "payable"
|
||||||
|
if newRest, found := parseKeyword(rest, "payable"); found {
|
||||||
|
rest = newRest
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(ABIMarshaling)
|
||||||
|
result["type"] = "receive"
|
||||||
|
result["stateMutability"] = stateMutability
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
||||||
unescapedSelector = skipWhitespace(unescapedSelector)
|
unescapedSelector = skipWhitespace(unescapedSelector)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue