From eb284ced68410e0859c5b67a237f4e27e92159a0 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 16 Sep 2024 19:38:56 -0400 Subject: [PATCH] Add utility for parsing errors, including as a string Signed-off-by: Peter Broadhurst --- pkg/abi/abi.go | 50 +++++++++++++++++++++++++++++++++++++++ pkg/abi/abi_test.go | 45 +++++++++++++++++++++++++++++++++++ pkg/abi/signedi256.go | 6 ++--- pkg/abi/typecomponents.go | 5 +++- 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index ef0466a4..1b384c90 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -315,6 +315,56 @@ func (a ABI) Errors() map[string]*Entry { return m } +// Returns the components value from the parsed error +func (a ABI) ParseError(revertData []byte) (*Entry, *ComponentValue, bool) { + return a.ParseErrorCtx(context.Background(), revertData) +} + +// Returns the components value from the parsed error +func (a ABI) ParseErrorCtx(ctx context.Context, revertData []byte) (*Entry, *ComponentValue, bool) { + // Always include the default error + a = append(ABI{ + {Type: Error, Name: "Error", Inputs: ParameterArray{{Name: "reason", Type: "string"}}}, + }, a...) + for _, e := range a { + if e.Type == Error { + if cv, err := e.DecodeCallDataCtx(ctx, revertData); err == nil { + return e, cv, true + } + } + } + return nil, nil, false +} + +func (a ABI) ErrorString(revertData []byte) (string, bool) { + return a.ErrorStringCtx(context.Background(), revertData) +} + +func (a ABI) ErrorStringCtx(ctx context.Context, revertData []byte) (string, bool) { + var parsed []interface{} + e, cv, ok := a.ParseErrorCtx(ctx, revertData) + if ok { + if res, err := NewSerializer().SetFormattingMode(FormatAsFlatArrays).SerializeInterfaceCtx(ctx, cv); err == nil { + parsed, ok = res.([]interface{}) + } + } + if !ok || parsed == nil { + return "", false + } + buff := new(bytes.Buffer) + buff.WriteString(e.Name) + buff.WriteRune('(') + for i, c := range parsed { + if i > 0 { + buff.WriteRune(',') + } + b, _ := json.Marshal(c) + buff.Write(b) + } + buff.WriteRune(')') + return buff.String(), true +} + // Validate processes all the components of all the parameters in this ABI entry func (e *Entry) Validate() (err error) { return e.ValidateCtx(context.Background()) diff --git a/pkg/abi/abi_test.go b/pkg/abi/abi_test.go index 87ed8b1b..c551fc19 100644 --- a/pkg/abi/abi_test.go +++ b/pkg/abi/abi_test.go @@ -1027,3 +1027,48 @@ func TestComplexStructSolidityDef(t *testing.T) { }, childStructs) } + +func TestErrorString(t *testing.T) { + + customErrABI := ABI{ + { + Type: Error, + Name: "ExampleError", + Inputs: ParameterArray{ + { + Name: "param1", + Type: "string", + }, + { + Name: "param2", + Type: "uint256", + }, + }, + }, + } + + revertReason, err := customErrABI[0].EncodeCallDataJSON([]byte(`{"param1":"test1","param2":12345}`)) + assert.NoError(t, err) + + errString, ok := customErrABI.ErrorString(revertReason) + assert.True(t, ok) + assert.Equal(t, `ExampleError("test1","12345")`, errString) + + e, cv, ok := customErrABI.ParseError(revertReason) + assert.True(t, ok) + assert.NotNil(t, e) + assert.NotNil(t, cv) + + exampleDefaultError := ethtypes.MustNewHexBytes0xPrefix(`0x08c379a0` + + `0000000000000000000000000000000000000000000000000000000000000020` + + `000000000000000000000000000000000000000000000000000000000000001a` + + `4e6f7420656e6f7567682045746865722070726f76696465642e000000000000`) + errString, ok = customErrABI.ErrorString(exampleDefaultError) + assert.True(t, ok) + assert.Equal(t, `Error("Not enough Ether provided.")`, errString) + + mismatchError := ethtypes.MustNewHexBytes0xPrefix(`0x11223344`) + _, ok = customErrABI.ErrorString(mismatchError) + assert.False(t, ok) + +} diff --git a/pkg/abi/signedi256.go b/pkg/abi/signedi256.go index 1e11856d..9e2324b3 100644 --- a/pkg/abi/signedi256.go +++ b/pkg/abi/signedi256.go @@ -26,9 +26,9 @@ var posMax = map[uint16]*big.Int{} var negMax = map[uint16]*big.Int{} func init() { - for i := 8; i <= 256; i += 8 { - posMax[uint16(i)] = maxPositiveSignedInt(uint(i)) - negMax[uint16(i)] = maxNegativeSignedInt(uint(i)) + for i := uint16(8); i <= uint16(256); i += 8 { + posMax[i] = maxPositiveSignedInt(uint(i)) + negMax[i] = maxNegativeSignedInt(uint(i)) } } diff --git a/pkg/abi/typecomponents.go b/pkg/abi/typecomponents.go index 91ebda60..28324061 100644 --- a/pkg/abi/typecomponents.go +++ b/pkg/abi/typecomponents.go @@ -630,6 +630,7 @@ func parseMSuffix(ctx context.Context, abiTypeString string, ec *typeComponent, if err != nil { return i18n.WrapError(ctx, err, signermsgs.MsgInvalidABISuffix, abiTypeString, ec.elementaryType) } + //nolint:gosec // we used bitSize on ParseUint above ec.m = uint16(val) if ec.m < ec.elementaryType.mMin || ec.m > ec.elementaryType.mMax { return i18n.NewError(ctx, signermsgs.MsgInvalidABISuffix, abiTypeString, ec.elementaryType) @@ -646,6 +647,7 @@ func parseNSuffix(ctx context.Context, abiTypeString string, ec *typeComponent, if err != nil { return i18n.WrapError(ctx, err, signermsgs.MsgInvalidABISuffix, abiTypeString, ec.elementaryType) } + //nolint:gosec // we used bitSize on ParseUint above ec.n = uint16(val) if ec.n < ec.elementaryType.nMin || ec.n > ec.elementaryType.nMax { return i18n.NewError(ctx, signermsgs.MsgInvalidABISuffix, abiTypeString, ec.elementaryType) @@ -672,10 +674,11 @@ func parseMxNSuffix(ctx context.Context, abiTypeString string, ec *typeComponent // parseArrayM parses the "8" in "uint256[8]" for a fixed length array of [M] func parseArrayM(ctx context.Context, abiTypeString string, ac *typeComponent, mStr string) error { - val, err := strconv.ParseUint(mStr, 10, 64) + val, err := strconv.ParseUint(mStr, 10, 32) if err != nil { return i18n.WrapError(ctx, err, signermsgs.MsgInvalidABIArraySpec, abiTypeString) } + //nolint:gosec // we used bitSize on ParseUint above ac.arrayLength = int(val) return nil }