github.com/ethereum/go-ethereum@v1.16.1/accounts/abi/abi.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package abi
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"math/big"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/crypto"
    29  )
    30  
    31  // The ABI holds information about a contract's context and available
    32  // invocable methods. It will allow you to type check function calls and
    33  // packs data accordingly.
    34  type ABI struct {
    35  	Constructor Method
    36  	Methods     map[string]Method
    37  	Events      map[string]Event
    38  	Errors      map[string]Error
    39  
    40  	// Additional "special" functions introduced in solidity v0.6.0.
    41  	// It's separated from the original default fallback. Each contract
    42  	// can only define one fallback and receive function.
    43  	Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
    44  	Receive  Method
    45  }
    46  
    47  // JSON returns a parsed ABI interface and error if it failed.
    48  func JSON(reader io.Reader) (ABI, error) {
    49  	dec := json.NewDecoder(reader)
    50  
    51  	var abi ABI
    52  	if err := dec.Decode(&abi); err != nil {
    53  		return ABI{}, err
    54  	}
    55  	return abi, nil
    56  }
    57  
    58  // Pack the given method name to conform the ABI. Method call's data
    59  // will consist of method_id, args0, arg1, ... argN. Method id consists
    60  // of 4 bytes and arguments are all 32 bytes.
    61  // Method ids are created from the first 4 bytes of the hash of the
    62  // methods string signature. (signature = baz(uint32,string32))
    63  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    64  	// Fetch the ABI of the requested method
    65  	if name == "" {
    66  		// constructor
    67  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		return arguments, nil
    72  	}
    73  	method, exist := abi.Methods[name]
    74  	if !exist {
    75  		return nil, fmt.Errorf("method '%s' not found", name)
    76  	}
    77  	arguments, err := method.Inputs.Pack(args...)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	// Pack up the method ID too if not a constructor and return
    82  	return append(method.ID, arguments...), nil
    83  }
    84  
    85  func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
    86  	// since there can't be naming collisions with contracts and events,
    87  	// we need to decide whether we're calling a method, event or an error
    88  	var args Arguments
    89  	if method, ok := abi.Methods[name]; ok {
    90  		if len(data)%32 != 0 {
    91  			return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
    92  		}
    93  		args = method.Outputs
    94  	}
    95  	if event, ok := abi.Events[name]; ok {
    96  		args = event.Inputs
    97  	}
    98  	if err, ok := abi.Errors[name]; ok {
    99  		args = err.Inputs
   100  	}
   101  	if args == nil {
   102  		return nil, fmt.Errorf("abi: could not locate named method, event or error: %s", name)
   103  	}
   104  	return args, nil
   105  }
   106  
   107  // Unpack unpacks the output according to the abi specification.
   108  func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
   109  	args, err := abi.getArguments(name, data)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return args.Unpack(data)
   114  }
   115  
   116  // UnpackIntoInterface unpacks the output in v according to the abi specification.
   117  // It performs an additional copy. Please only use, if you want to unpack into a
   118  // structure that does not strictly conform to the abi structure (e.g. has additional arguments)
   119  func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
   120  	args, err := abi.getArguments(name, data)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	unpacked, err := args.Unpack(data)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	return args.Copy(v, unpacked)
   129  }
   130  
   131  // UnpackIntoMap unpacks a log into the provided map[string]interface{}.
   132  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
   133  	args, err := abi.getArguments(name, data)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	return args.UnpackIntoMap(v, data)
   138  }
   139  
   140  // UnmarshalJSON implements json.Unmarshaler interface.
   141  func (abi *ABI) UnmarshalJSON(data []byte) error {
   142  	var fields []struct {
   143  		Type    string
   144  		Name    string
   145  		Inputs  []Argument
   146  		Outputs []Argument
   147  
   148  		// Status indicator which can be: "pure", "view",
   149  		// "nonpayable" or "payable".
   150  		StateMutability string
   151  
   152  		// Deprecated Status indicators, but removed in v0.6.0.
   153  		Constant bool // True if function is either pure or view
   154  		Payable  bool // True if function is payable
   155  
   156  		// Event relevant indicator represents the event is
   157  		// declared as anonymous.
   158  		Anonymous bool
   159  	}
   160  	if err := json.Unmarshal(data, &fields); err != nil {
   161  		return err
   162  	}
   163  	abi.Methods = make(map[string]Method)
   164  	abi.Events = make(map[string]Event)
   165  	abi.Errors = make(map[string]Error)
   166  	for _, field := range fields {
   167  		switch field.Type {
   168  		case "constructor":
   169  			abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
   170  		case "function":
   171  			name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
   172  			abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
   173  		case "fallback":
   174  			// New introduced function type in v0.6.0, check more detail
   175  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   176  			if abi.HasFallback() {
   177  				return errors.New("only single fallback is allowed")
   178  			}
   179  			abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
   180  		case "receive":
   181  			// New introduced function type in v0.6.0, check more detail
   182  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   183  			if abi.HasReceive() {
   184  				return errors.New("only single receive is allowed")
   185  			}
   186  			if field.StateMutability != "payable" {
   187  				return errors.New("the statemutability of receive can only be payable")
   188  			}
   189  			abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
   190  		case "event":
   191  			name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
   192  			abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
   193  		case "error":
   194  			// Errors cannot be overloaded or overridden but are inherited,
   195  			// no need to resolve the name conflict here.
   196  			abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
   197  		default:
   198  			return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
   199  		}
   200  	}
   201  	return nil
   202  }
   203  
   204  // MethodById looks up a method by the 4-byte id,
   205  // returns nil if none found.
   206  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   207  	if len(sigdata) < 4 {
   208  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   209  	}
   210  	for _, method := range abi.Methods {
   211  		if bytes.Equal(method.ID, sigdata[:4]) {
   212  			return &method, nil
   213  		}
   214  	}
   215  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   216  }
   217  
   218  // EventByID looks an event up by its topic hash in the
   219  // ABI and returns nil if none found.
   220  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   221  	for _, event := range abi.Events {
   222  		if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
   223  			return &event, nil
   224  		}
   225  	}
   226  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   227  }
   228  
   229  // ErrorByID looks up an error by the 4-byte id,
   230  // returns nil if none found.
   231  func (abi *ABI) ErrorByID(sigdata [4]byte) (*Error, error) {
   232  	for _, errABI := range abi.Errors {
   233  		if bytes.Equal(errABI.ID[:4], sigdata[:]) {
   234  			return &errABI, nil
   235  		}
   236  	}
   237  	return nil, fmt.Errorf("no error with id: %#x", sigdata[:])
   238  }
   239  
   240  // HasFallback returns an indicator whether a fallback function is included.
   241  func (abi *ABI) HasFallback() bool {
   242  	return abi.Fallback.Type == Fallback
   243  }
   244  
   245  // HasReceive returns an indicator whether a receive function is included.
   246  func (abi *ABI) HasReceive() bool {
   247  	return abi.Receive.Type == Receive
   248  }
   249  
   250  // revertSelector is a special function selector for revert reason unpacking.
   251  var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
   252  
   253  // panicSelector is a special function selector for panic reason unpacking.
   254  var panicSelector = crypto.Keccak256([]byte("Panic(uint256)"))[:4]
   255  
   256  // panicReasons map is for readable panic codes
   257  // see this linkage for the details
   258  // https://docs.soliditylang.org/en/v0.8.21/control-structures.html#panic-via-assert-and-error-via-require
   259  // the reason string list is copied from ether.js
   260  // https://github.com/ethers-io/ethers.js/blob/fa3a883ff7c88611ce766f58bdd4b8ac90814470/src.ts/abi/interface.ts#L207-L218
   261  var panicReasons = map[uint64]string{
   262  	0x00: "generic panic",
   263  	0x01: "assert(false)",
   264  	0x11: "arithmetic underflow or overflow",
   265  	0x12: "division or modulo by zero",
   266  	0x21: "enum overflow",
   267  	0x22: "invalid encoded storage byte array accessed",
   268  	0x31: "out-of-bounds array access; popping on an empty array",
   269  	0x32: "out-of-bounds access of an array or bytesN",
   270  	0x41: "out of memory",
   271  	0x51: "uninitialized function",
   272  }
   273  
   274  // UnpackRevert resolves the abi-encoded revert reason. According to the solidity
   275  // spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
   276  // the provided revert reason is abi-encoded as if it were a call to function
   277  // `Error(string)` or `Panic(uint256)`. So it's a special tool for it.
   278  func UnpackRevert(data []byte) (string, error) {
   279  	if len(data) < 4 {
   280  		return "", errors.New("invalid data for unpacking")
   281  	}
   282  	switch {
   283  	case bytes.Equal(data[:4], revertSelector):
   284  		typ, err := NewType("string", "", nil)
   285  		if err != nil {
   286  			return "", err
   287  		}
   288  		unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
   289  		if err != nil {
   290  			return "", err
   291  		}
   292  		return unpacked[0].(string), nil
   293  	case bytes.Equal(data[:4], panicSelector):
   294  		typ, err := NewType("uint256", "", nil)
   295  		if err != nil {
   296  			return "", err
   297  		}
   298  		unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
   299  		if err != nil {
   300  			return "", err
   301  		}
   302  		pCode := unpacked[0].(*big.Int)
   303  		// uint64 safety check for future
   304  		// but the code is not bigger than MAX(uint64) now
   305  		if pCode.IsUint64() {
   306  			if reason, ok := panicReasons[pCode.Uint64()]; ok {
   307  				return reason, nil
   308  			}
   309  		}
   310  		return fmt.Sprintf("unknown panic code: %#x", pCode), nil
   311  	default:
   312  		return "", errors.New("invalid data for unpacking")
   313  	}
   314  }