-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
700 additions
and
1,016 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package abi | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
) | ||
|
||
func NewTypeFromString(s string) (Type, error) { | ||
if strings.HasPrefix(s, "tuple") { | ||
sig, args, err := newTypeForTuple(s) | ||
if err != nil { | ||
return Type{}, err | ||
} | ||
return NewType(sig, "", args) | ||
} | ||
return NewType(s, "", nil) | ||
} | ||
|
||
func (t Type) Pack(v interface{}) ([]byte, error) { | ||
return t.pack(reflect.ValueOf(v)) | ||
} | ||
|
||
func (t Type) Unpack(data []byte, obj interface{}) error { | ||
fmt.Println(toGoType(0, t, data)) | ||
|
||
return nil | ||
} | ||
|
||
// newTypeForTuple implements the format described in https://blog.ricmoo.com/human-readable-contract-abis-in-ethers-js-141902f4d917 | ||
func newTypeForTuple(s string) (string, []ArgumentMarshaling, error) { | ||
if !strings.HasPrefix(s, "tuple") { | ||
return "", nil, fmt.Errorf("'tuple' prefix not found") | ||
} | ||
|
||
pos := strings.Index(s, "(") | ||
if pos == -1 { | ||
return "", nil, fmt.Errorf("not a tuple, '(' not found") | ||
} | ||
|
||
// this is the type of the tuple. It can either be | ||
// tuple, tuple[] or tuple[x] | ||
sig := s[:pos] | ||
s = s[pos:] | ||
|
||
// Now, decode the arguments of the tuple | ||
// tuple(arg1, arg2, tuple(arg3, arg4)). | ||
// We need to find the commas that are not inside a nested tuple. | ||
// We do this by keeping a counter of the number of open parens. | ||
|
||
var ( | ||
parenthesisCount int | ||
fields []string | ||
) | ||
|
||
lastComma := 1 | ||
for indx, c := range s { | ||
switch c { | ||
case '(': | ||
parenthesisCount++ | ||
case ')': | ||
parenthesisCount-- | ||
if parenthesisCount == 0 { | ||
fields = append(fields, s[lastComma:indx]) | ||
|
||
// this should be the end of the tuple | ||
if indx != len(s)-1 { | ||
return "", nil, fmt.Errorf("invalid tuple, it does not end with ')'") | ||
} | ||
} | ||
case ',': | ||
if parenthesisCount == 1 { | ||
fields = append(fields, s[lastComma:indx]) | ||
lastComma = indx + 1 | ||
} | ||
} | ||
} | ||
|
||
// trim the args of spaces | ||
for i := range fields { | ||
fields[i] = strings.TrimSpace(fields[i]) | ||
} | ||
|
||
// decode the type of each field | ||
var args []ArgumentMarshaling | ||
for _, field := range fields { | ||
// anonymous fields are not supported so the first | ||
// string should be the identifier of the field. | ||
|
||
spacePos := strings.Index(field, " ") | ||
if spacePos == -1 { | ||
return "", nil, fmt.Errorf("invalid tuple field name not found '%s'", field) | ||
} | ||
|
||
name := field[:spacePos] | ||
field = field[spacePos+1:] | ||
|
||
if strings.HasPrefix(field, "tuple") { | ||
// decode a recursive tuple | ||
sig, elems, err := newTypeForTuple(field) | ||
if err != nil { | ||
return "", nil, err | ||
} | ||
args = append(args, ArgumentMarshaling{ | ||
Name: name, | ||
Type: sig, | ||
Components: elems, | ||
}) | ||
} else { | ||
// basic type. Try to decode it to see | ||
// if it is a correct abi type. | ||
if _, err := NewType(field, "", nil); err != nil { | ||
return "", nil, fmt.Errorf("invalid tuple basic field '%s': %v", field, err) | ||
} | ||
args = append(args, ArgumentMarshaling{ | ||
Name: name, | ||
Type: field, | ||
}) | ||
} | ||
} | ||
|
||
return sig, args, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package abi | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestNewType2FromString(t *testing.T) { | ||
type struct1 struct { | ||
A uint64 | ||
B []struct { | ||
X struct { | ||
C uint64 | ||
} | ||
D uint64 | ||
} | ||
E struct { | ||
F uint64 | ||
G uint64 | ||
} | ||
} | ||
|
||
cases := []struct { | ||
input string | ||
want string | ||
obj interface{} | ||
}{ | ||
{ | ||
"uint64[]", | ||
"uint64[]", | ||
[]uint64{1, 2, 3}, | ||
}, | ||
{ | ||
"tuple(a uint64, b uint64)", | ||
"(uint64,uint64)", | ||
&struct { | ||
A uint64 | ||
B uint64 | ||
}{A: 1, B: 2}, | ||
}, | ||
{ | ||
"tuple(a uint64, b tuple(c uint64), d uint64)", | ||
"(uint64,(uint64),uint64)", | ||
&struct { | ||
A uint64 | ||
B struct { | ||
C uint64 | ||
} | ||
D uint64 | ||
}{A: 1, B: struct{ C uint64 }{C: 2}, D: 3}, | ||
}, | ||
{ | ||
"tuple(a uint64, b tuple[](x tuple(c uint64), d uint64), e tuple(f uint64, g uint64))", | ||
"(uint64,((uint64),uint64)[],(uint64,uint64))", | ||
&struct1{ | ||
A: 1, | ||
B: []struct { | ||
X struct { | ||
C uint64 | ||
} | ||
D uint64 | ||
}{ | ||
{ | ||
X: struct { | ||
C uint64 | ||
}{C: 2}, | ||
D: 3, | ||
}, | ||
{ | ||
X: struct { | ||
C uint64 | ||
}{C: 4}, | ||
D: 5, | ||
}, | ||
}, | ||
E: struct { | ||
F uint64 | ||
G uint64 | ||
}{F: 6, G: 7}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
typ, err := NewTypeFromString(c.input) | ||
|
||
require.NoError(t, err) | ||
require.Equal(t, c.want, typ.String()) | ||
|
||
_, err = typ.Pack(c.obj) | ||
require.NoError(t, err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.