diff --git a/pkg/utils/decode/bytes.go b/pkg/utils/decode/bytes.go new file mode 100644 index 0000000..4f92530 --- /dev/null +++ b/pkg/utils/decode/bytes.go @@ -0,0 +1,39 @@ +package decode + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// ParseBytes20 parses `string` into `[20]byte` type. The input string must have +// 20 bytes hex-encoded. +func ParseBytes20(str string) ([20]byte, error) { + bytesArray := [20]byte{} + slice, err := hexutil.Decode(str) + if err != nil { + return bytesArray, err + } + if len(slice) != 20 { + return bytesArray, fmt.Errorf("expected 20 bytes array; has: [%v]", len(slice)) + } + + copy(bytesArray[:], slice) + return bytesArray, nil +} + +// ParseBytes32 parses `string` into `[32]byte` type. The input string must have +// 32 bytes hex-encoded. +func ParseBytes32(str string) ([32]byte, error) { + bytesArray := [32]byte{} + slice, err := hexutil.Decode(str) + if err != nil { + return bytesArray, err + } + if len(slice) != 32 { + return bytesArray, fmt.Errorf("expected 32 bytes array; has: [%v]", len(slice)) + } + + copy(bytesArray[:], slice) + return bytesArray, nil +} diff --git a/pkg/utils/decode/bytes_test.go b/pkg/utils/decode/bytes_test.go new file mode 100644 index 0000000..96b774b --- /dev/null +++ b/pkg/utils/decode/bytes_test.go @@ -0,0 +1,126 @@ +package decode + +import ( + "fmt" + "testing" +) + +func TestParseBytes20(t *testing.T) { + hexEncoded := "0x3805eed0bb0792eff8815addedb36add2c7257e5" + bytes, err := ParseBytes20(hexEncoded) + if err != nil { + t.Fatal(err) + } + + roundtrip := fmt.Sprintf("0x%x", bytes) + + if roundtrip != hexEncoded { + t.Errorf( + "unexpected parsed bytes\nexpected: %v\nactual: %v\n", + hexEncoded, + roundtrip, + ) + } +} + +func TestParseBytes20_Fail(t *testing.T) { + + var tests = map[string]struct { + input string + expectedError string + }{ + "too short": { + input: "0xFFFF", + expectedError: "expected 20 bytes array; has: [2]", + }, + "empty": { + input: "", + expectedError: "empty hex string", + }, + "just 0x prefix": { + input: "0x", + expectedError: "expected 20 bytes array; has: [0]", + }, + "no prefix": { + input: "FF", + expectedError: "hex string without 0x prefix", + }, + "invalid hex": { + input: "0xLMA0", + expectedError: "invalid hex string", + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + _, actualError := ParseBytes20(test.input) + if actualError.Error() != test.expectedError { + t.Errorf( + "unexpected error\nexpected: %v\nactual: %v\n", + test.expectedError, + actualError, + ) + } + }) + } +} + +func TestParseBytes32(t *testing.T) { + hexEncoded := "0xad63a8286ea7fa22d75e167216171417a96c4753946fd45e3a8dff4e4f29a830" + bytes, err := ParseBytes32(hexEncoded) + if err != nil { + t.Fatal(err) + } + + roundtrip := fmt.Sprintf("0x%x", bytes) + + if roundtrip != hexEncoded { + t.Errorf( + "unexpected parsed bytes\nexpected: %v\nactual: %v\n", + hexEncoded, + roundtrip, + ) + } +} + +func TestParseBytes32_Fail(t *testing.T) { + + var tests = map[string]struct { + input string + expectedError string + }{ + "too short": { + input: "0xFFFF", + expectedError: "expected 32 bytes array; has: [2]", + }, + "empty": { + input: "", + expectedError: "empty hex string", + }, + "just 0x prefix": { + input: "0x", + expectedError: "expected 32 bytes array; has: [0]", + }, + "no prefix": { + input: "FF", + expectedError: "hex string without 0x prefix", + }, + "invalid hex": { + input: "0xLMA0", + expectedError: "invalid hex string", + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + _, actualError := ParseBytes32(test.input) + if actualError.Error() != test.expectedError { + t.Errorf( + "unexpected error\nexpected: %v\nactual: %v\n", + test.expectedError, + actualError, + ) + } + }) + } +} diff --git a/tools/generators/ethereum/contract_parsing.go b/tools/generators/ethereum/contract_parsing.go index 0a3cbfb..9f1b47e 100644 --- a/tools/generators/ethereum/contract_parsing.go +++ b/tools/generators/ethereum/contract_parsing.go @@ -231,6 +231,10 @@ func buildMethodInfo( switch goType { case "[]byte": cmdParsingFn = "hexutil.Decode(%s)" + case "[20]byte": + cmdParsingFn = "decode.ParseBytes20(%s)" + case "[32]byte": + cmdParsingFn = "decode.ParseBytes32(%s)" case "common.Address": cmdParsingFn = "chainutil.AddressFromHex(%s)" case "*big.Int":