-
Notifications
You must be signed in to change notification settings - Fork 1
/
error.go
150 lines (128 loc) · 3.94 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package abi
import (
"fmt"
"github.com/defiweb/go-eth/crypto"
)
// CustomError represents an custom error returned by a contract call.
type CustomError struct {
Type *Error // The error type.
Data []byte // The error data returned by the contract call.
}
// Error implements the error interface.
func (e CustomError) Error() string {
return fmt.Sprintf("error: %s", e.Type.Name())
}
// Error represents an error in an ABI. The error can be used to decode errors
// returned by a contract call.
type Error struct {
name string
inputs *TupleType
config *ABI
fourBytes FourBytes
signature string
}
// NewError creates a new Error instance.
func NewError(name string, inputs *TupleType) *Error {
return Default.NewError(name, inputs)
}
// ParseError parses an error signature and returns a new Error.
//
// An error signature is similar to a method signature, but returns no values.
// It can be optionally prefixed with the "error" keyword.
//
// The following examples are valid signatures:
//
// foo((uint256,bytes32)[])
// foo((uint256 a, bytes32 b)[] c)
// error foo(tuple(uint256 a, bytes32 b)[] c)
//
// This function is equivalent to calling Parser.ParseError with the default
// configuration.
func ParseError(signature string) (*Error, error) {
return Default.ParseError(signature)
}
// MustParseError is like ParseError but panics on error.
func MustParseError(signature string) *Error {
e, err := ParseError(signature)
if err != nil {
panic(err)
}
return e
}
// NewError creates a new Error instance.
func (a *ABI) NewError(name string, inputs *TupleType) *Error {
m := &Error{
name: name,
inputs: inputs,
config: a,
}
m.generateSignature()
m.calculateFourBytes()
return m
}
// ParseError parses an error signature and returns a new Error.
//
// See ParseError for more information.
func (a *ABI) ParseError(signature string) (*Error, error) {
return parseError(a, signature)
}
// Name returns the name of the error.
func (m *Error) Name() string {
return m.name
}
// Inputs returns the input arguments of the error as a tuple type.
func (m *Error) Inputs() *TupleType {
return m.inputs
}
// FourBytes is the first four bytes of the Keccak256 hash of the error
// signature.
func (m *Error) FourBytes() FourBytes {
return m.fourBytes
}
// Signature returns the error signature, that is, the error name and the
// canonical type of error arguments.
func (m *Error) Signature() string {
return m.signature
}
// Is returns true if the ABI encoded data is an error of this type.
func (m *Error) Is(data []byte) bool {
return m.fourBytes.Match(data) && (len(data)-4)%WordLength == 0
}
// DecodeValue decodes the error into a map or structure. If a structure is
// given, it must have fields with the same names as error arguments.
func (m *Error) DecodeValue(data []byte, val any) error {
if m.fourBytes.Match(data) {
return fmt.Errorf("abi: selector mismatch for error %s", m.name)
}
return m.config.DecodeValue(m.inputs, data[4:], val)
}
// DecodeValues decodes the error into a map or structure. If a structure is
// given, it must have fields with the same names as error arguments.
func (m *Error) DecodeValues(data []byte, vals ...any) error {
if m.fourBytes.Match(data) {
return fmt.Errorf("abi: selector mismatch for error %s", m.name)
}
return m.config.DecodeValues(m.inputs, data[4:], vals...)
}
// ToError converts the error data returned by contract calls into a CustomError.
// If the data does not contain a valid error message, it returns nil.
func (m *Error) ToError(data []byte) error {
if !m.fourBytes.Match(data) {
return nil
}
return CustomError{
Type: m,
Data: data[4:],
}
}
// String returns the human-readable signature of the error.
func (m *Error) String() string {
return "error " + m.name + m.inputs.String()
}
func (m *Error) generateSignature() {
m.signature = m.name + m.inputs.CanonicalType()
}
func (m *Error) calculateFourBytes() {
id := crypto.Keccak256([]byte(m.Signature()))
copy(m.fourBytes[:], id[:4])
}