Skip to content

Commit 0b63915

Browse files
accounts/abi: allow overloaded argument names (#21060)
* accounts/abi: allow overloaded argument names In solidity it is possible to create the following contract: ``` contract Overloader { struct F { uint _f; uint __f; uint f; } function f(F memory f) public {} } ``` This however resulted in a panic in the abi package. * accounts/abi fixed error handling
1 parent b8ea904 commit 0b63915

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

accounts/abi/abi_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ const jsondata = `
5757
{ "type" : "function", "name" : "fixedArrBytes", "stateMutability" : "view", "inputs" : [ { "name" : "bytes", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
5858
{ "type" : "function", "name" : "mixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" } ] },
5959
{ "type" : "function", "name" : "doubleFixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] },
60-
{ "type" : "function", "name" : "multipleMixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] }
60+
{ "type" : "function", "name" : "multipleMixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] },
61+
{ "type" : "function", "name" : "overloadedNames", "stateMutability" : "view", "inputs": [ { "components": [ { "internalType": "uint256", "name": "_f", "type": "uint256" }, { "internalType": "uint256", "name": "__f", "type": "uint256"}, { "internalType": "uint256", "name": "f", "type": "uint256"}],"internalType": "struct Overloader.F", "name": "f","type": "tuple"}]}
6162
]`
6263

6364
var (
@@ -80,6 +81,10 @@ var (
8081
Uint256ArrNested, _ = NewType("uint256[2][2]", "", nil)
8182
Uint8ArrNested, _ = NewType("uint8[][2]", "", nil)
8283
Uint8SliceNested, _ = NewType("uint8[][]", "", nil)
84+
TupleF, _ = NewType("tuple", "struct Overloader.F", []ArgumentMarshaling{
85+
{Name: "_f", Type: "uint256"},
86+
{Name: "__f", Type: "uint256"},
87+
{Name: "f", Type: "uint256"}})
8388
)
8489

8590
var methods = map[string]Method{
@@ -108,6 +113,7 @@ var methods = map[string]Method{
108113
"mixedArrStr": NewMethod("mixedArrStr", "mixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr", Uint256Arr2, false}, {"dynArr", Uint256Arr, false}}, nil),
109114
"doubleFixedArrStr": NewMethod("doubleFixedArrStr", "doubleFixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr1", Uint256Arr2, false}, {"fixedArr2", Uint256Arr3, false}}, nil),
110115
"multipleMixedArrStr": NewMethod("multipleMixedArrStr", "multipleMixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr1", Uint256Arr2, false}, {"dynArr", Uint256Arr, false}, {"fixedArr2", Uint256Arr3, false}}, nil),
116+
"overloadedNames": NewMethod("overloadedNames", "overloadedNames", Function, "view", false, false, []Argument{{"f", TupleF, false}}, nil),
111117
}
112118

113119
func TestReader(t *testing.T) {
@@ -117,7 +123,7 @@ func TestReader(t *testing.T) {
117123

118124
exp, err := JSON(strings.NewReader(jsondata))
119125
if err != nil {
120-
t.Error(err)
126+
t.Fatal(err)
121127
}
122128

123129
for name, expM := range exp.Methods {

accounts/abi/type.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,19 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
163163
expression string // canonical parameter expression
164164
)
165165
expression += "("
166+
overloadedNames := make(map[string]string)
166167
for idx, c := range components {
167168
cType, err := NewType(c.Type, c.InternalType, c.Components)
168169
if err != nil {
169170
return Type{}, err
170171
}
171-
if ToCamelCase(c.Name) == "" {
172-
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
172+
fieldName, err := overloadedArgName(c.Name, overloadedNames)
173+
if err != nil {
174+
return Type{}, err
173175
}
176+
overloadedNames[fieldName] = fieldName
174177
fields = append(fields, reflect.StructField{
175-
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
178+
Name: fieldName, // reflect.StructOf will panic for any exported field.
176179
Type: cType.getType(),
177180
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
178181
})
@@ -246,6 +249,20 @@ func (t Type) getType() reflect.Type {
246249
}
247250
}
248251

252+
func overloadedArgName(rawName string, names map[string]string) (string, error) {
253+
fieldName := ToCamelCase(rawName)
254+
if fieldName == "" {
255+
return "", errors.New("abi: purely anonymous or underscored field is not supported")
256+
}
257+
// Handle overloaded fieldNames
258+
_, ok := names[fieldName]
259+
for idx := 0; ok; idx++ {
260+
fieldName = fmt.Sprintf("%s%d", ToCamelCase(rawName), idx)
261+
_, ok = names[fieldName]
262+
}
263+
return fieldName, nil
264+
}
265+
249266
// String implements Stringer
250267
func (t Type) String() (out string) {
251268
return t.stringKind

0 commit comments

Comments
 (0)