github.com/ethereum/go-ethereum@v1.14.3/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 or an event
    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 args == nil {
    99  		return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
   100  	}
   101  	return args, nil
   102  }
   103  
   104  // Unpack unpacks the output according to the abi specification.
   105  func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
   106  	args, err := abi.getArguments(name, data)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	return args.Unpack(data)
   111  }
   112  
   113  // UnpackIntoInterface unpacks the output in v according to the abi specification.
   114  // It performs an additional copy. Please only use, if you want to unpack into a
   115  // structure that does not strictly conform to the abi structure (e.g. has additional arguments)
   116  func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
   117  	args, err := abi.getArguments(name, data)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	unpacked, err := args.Unpack(data)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	return args.Copy(v, unpacked)
   126  }
   127  
   128  // UnpackIntoMap unpacks a log into the provided map[string]interface{}.
   129  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
   130  	args, err := abi.getArguments(name, data)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	return args.UnpackIntoMap(v, data)
   135  }
   136  
   137  // UnmarshalJSON implements json.Unmarshaler interface.
   138  func (abi *ABI) UnmarshalJSON(data []byte) error {
   139  	var fields []struct {
   140  		Type    string
   141  		Name    string
   142  		Inputs  []Argument
   143  		Outputs []Argument
   144  
   145  		// Status indicator which can be: "pure", "view",
   146  		// "nonpayable" or "payable".
   147  		StateMutability string
   148  
   149  		// Deprecated Status indicators, but removed in v0.6.0.
   150  		Constant bool // True if function is either pure or view
   151  		Payable  bool // True if function is payable
   152  
   153  		// Event relevant indicator represents the event is
   154  		// declared as anonymous.
   155  		Anonymous bool
   156  	}
   157  	if err := json.Unmarshal(data, &fields); err != nil {
   158  		return err
   159  	}
   160  	abi.Methods = make(map[string]Method)
   161  	abi.Events = make(map[string]Event)
   162  	abi.Errors = make(map[string]Error)
   163  	for _, field := range fields {
   164  		switch field.Type {
   165  		case "constructor":
   166  			abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
   167  		case "function":
   168  			name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
   169  			abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
   170  		case "fallback":
   171  			// New introduced function type in v0.6.0, check more detail
   172  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   173  			if abi.HasFallback() {
   174  				return errors.New("only single fallback is allowed")
   175  			}
   176  			abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
   177  		case "receive":
   178  			// New introduced function type in v0.6.0, check more detail
   179  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   180  			if abi.HasReceive() {
   181  				return errors.New("only single receive is allowed")
   182  			}
   183  			if field.StateMutability != "payable" {
   184  				return errors.New("the statemutability of receive can only be payable")
   185  			}
   186  			abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
   187  		case "event":
   188  			name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
   189  			abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
   190  		case "error":
   191  			// Errors cannot be overloaded or overridden but are inherited,
   192  			// no need to resolve the name conflict here.
   193  			abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
   194  		default:
   195  			return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
   196  		}
   197  	}
   198  	return nil
   199  }
   200  
   201  // MethodById looks up a method by the 4-byte id,
   202  // returns nil if none found.
   203  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   204  	if len(sigdata) < 4 {
   205  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   206  	}
   207  	for _, method := range abi.Methods {
   208  		if bytes.Equal(method.ID, sigdata[:4]) {
   209  			return &method, nil
   210  		}
   211  	}
   212  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   213  }
   214  
   215  // EventByID looks an event up by its topic hash in the
   216  // ABI and returns nil if none found.
   217  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   218  	for _, event := range abi.Events {
   219  		if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
   220  			return &event, nil
   221  		}
   222  	}
   223  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   224  }
   225  
   226  // ErrorByID looks up an error by the 4-byte id,
   227  // returns nil if none found.
   228  func (abi *ABI) ErrorByID(sigdata [4]byte) (*Error, error) {
   229  	for _, errABI := range abi.Errors {
   230  		if bytes.Equal(errABI.ID[:4], sigdata[:]) {
   231  			return &errABI, nil
   232  		}
   233  	}
   234  	return nil, fmt.Errorf("no error with id: %#x", sigdata[:])
   235  }
   236  
   237  // HasFallback returns an indicator whether a fallback function is included.
   238  func (abi *ABI) HasFallback() bool {
   239  	return abi.Fallback.Type == Fallback
   240  }
   241  
   242  // HasReceive returns an indicator whether a receive function is included.
   243  func (abi *ABI) HasReceive() bool {
   244  	return abi.Receive.Type == Receive
   245  }
   246  
   247  // revertSelector is a special function selector for revert reason unpacking.
   248  var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
   249  
   250  // panicSelector is a special function selector for panic reason unpacking.
   251  var panicSelector = crypto.Keccak256([]byte("Panic(uint256)"))[:4]
   252  
   253  // panicReasons map is for readable panic codes
   254  // see this linkage for the details
   255  // https://docs.soliditylang.org/en/v0.8.21/control-structures.html#panic-via-assert-and-error-via-require
   256  // the reason string list is copied from ether.js
   257  // https://github.com/ethers-io/ethers.js/blob/fa3a883ff7c88611ce766f58bdd4b8ac90814470/src.ts/abi/interface.ts#L207-L218
   258  var panicReasons = map[uint64]string{
   259  	0x00: "generic panic",
   260  	0x01: "assert(false)",
   261  	0x11: "arithmetic underflow or overflow",
   262  	0x12: "division or modulo by zero",
   263  	0x21: "enum overflow",
   264  	0x22: "invalid encoded storage byte array accessed",
   265  	0x31: "out-of-bounds array access; popping on an empty array",
   266  	0x32: "out-of-bounds access of an array or bytesN",
   267  	0x41: "out of memory",
   268  	0x51: "uninitialized function",
   269  }
   270  
   271  // UnpackRevert resolves the abi-encoded revert reason. According to the solidity
   272  // spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
   273  // the provided revert reason is abi-encoded as if it were a call to function
   274  // `Error(string)` or `Panic(uint256)`. So it's a special tool for it.
   275  func UnpackRevert(data []byte) (string, error) {
   276  	if len(data) < 4 {
   277  		return "", errors.New("invalid data for unpacking")
   278  	}
   279  	switch {
   280  	case bytes.Equal(data[:4], revertSelector):
   281  		typ, err := NewType("string", "", nil)
   282  		if err != nil {
   283  			return "", err
   284  		}
   285  		unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
   286  		if err != nil {
   287  			return "", err
   288  		}
   289  		return unpacked[0].(string), nil
   290  	case bytes.Equal(data[:4], panicSelector):
   291  		typ, err := NewType("uint256", "", nil)
   292  		if err != nil {
   293  			return "", err
   294  		}
   295  		unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
   296  		if err != nil {
   297  			return "", err
   298  		}
   299  		pCode := unpacked[0].(*big.Int)
   300  		// uint64 safety check for future
   301  		// but the code is not bigger than MAX(uint64) now
   302  		if pCode.IsUint64() {
   303  			if reason, ok := panicReasons[pCode.Uint64()]; ok {
   304  				return reason, nil
   305  			}
   306  		}
   307  		return fmt.Sprintf("unknown panic code: %#x", pCode), nil
   308  	default:
   309  		return "", errors.New("invalid data for unpacking")
   310  	}
   311  }