github.com/cgcardona/r-subnet-evm@v0.1.5/accounts/abi/abi.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2015 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package abi
    28  
    29  import (
    30  	"bytes"
    31  	"encoding/json"
    32  	"errors"
    33  	"fmt"
    34  	"io"
    35  
    36  	"github.com/ethereum/go-ethereum/common"
    37  	"github.com/ethereum/go-ethereum/crypto"
    38  )
    39  
    40  // The ABI holds information about a contract's context and available
    41  // invokable methods. It will allow you to type check function calls and
    42  // packs data accordingly.
    43  type ABI struct {
    44  	Constructor Method
    45  	Methods     map[string]Method
    46  	Events      map[string]Event
    47  	Errors      map[string]Error
    48  
    49  	// Additional "special" functions introduced in solidity v0.6.0.
    50  	// It's separated from the original default fallback. Each contract
    51  	// can only define one fallback and receive function.
    52  	Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
    53  	Receive  Method
    54  }
    55  
    56  // JSON returns a parsed ABI interface and error if it failed.
    57  func JSON(reader io.Reader) (ABI, error) {
    58  	dec := json.NewDecoder(reader)
    59  
    60  	var abi ABI
    61  	if err := dec.Decode(&abi); err != nil {
    62  		return ABI{}, err
    63  	}
    64  	return abi, nil
    65  }
    66  
    67  // Pack the given method name to conform the ABI. Method call's data
    68  // will consist of method_id, args0, arg1, ... argN. Method id consists
    69  // of 4 bytes and arguments are all 32 bytes.
    70  // Method ids are created from the first 4 bytes of the hash of the
    71  // methods string signature. (signature = baz(uint32,string32))
    72  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    73  	// Fetch the ABI of the requested method
    74  	if name == "" {
    75  		// constructor
    76  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		return arguments, nil
    81  	}
    82  	method, exist := abi.Methods[name]
    83  	if !exist {
    84  		return nil, fmt.Errorf("method '%s' not found", name)
    85  	}
    86  	arguments, err := method.Inputs.Pack(args...)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	// Pack up the method ID too if not a constructor and return
    91  	return append(method.ID, arguments...), nil
    92  }
    93  
    94  // PackEvent packs the given event name and arguments to conform the ABI.
    95  // Returns the topics for the event including the event signature (if non-anonymous event) and
    96  // hashes derived from indexed arguments and the packed data of non-indexed args according to
    97  // the event ABI specification.
    98  // The order of arguments must match the order of the event definition.
    99  // https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#indexed-event-encoding.
   100  // Note: PackEvent does not support array (fixed or dynamic-size) or struct types.
   101  func (abi ABI) PackEvent(name string, args ...interface{}) ([]common.Hash, []byte, error) {
   102  	event, exist := abi.Events[name]
   103  	if !exist {
   104  		return nil, nil, fmt.Errorf("event '%s' not found", name)
   105  	}
   106  	if len(args) != len(event.Inputs) {
   107  		return nil, nil, fmt.Errorf("event '%s' unexpected number of inputs %d", name, len(args))
   108  	}
   109  
   110  	var (
   111  		nonIndexedInputs = make([]interface{}, 0)
   112  		indexedInputs    = make([]interface{}, 0)
   113  		nonIndexedArgs   Arguments
   114  		indexedArgs      Arguments
   115  	)
   116  
   117  	for i, arg := range event.Inputs {
   118  		if arg.Indexed {
   119  			indexedArgs = append(indexedArgs, arg)
   120  			indexedInputs = append(indexedInputs, args[i])
   121  		} else {
   122  			nonIndexedArgs = append(nonIndexedArgs, arg)
   123  			nonIndexedInputs = append(nonIndexedInputs, args[i])
   124  		}
   125  	}
   126  
   127  	packedArguments, err := nonIndexedArgs.Pack(nonIndexedInputs...)
   128  	if err != nil {
   129  		return nil, nil, err
   130  	}
   131  	topics := make([]common.Hash, 0, len(indexedArgs)+1)
   132  	if !event.Anonymous {
   133  		topics = append(topics, event.ID)
   134  	}
   135  	indexedTopics, err := PackTopics(indexedInputs)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  
   140  	return append(topics, indexedTopics...), packedArguments, nil
   141  }
   142  
   143  // PackOutput packs the given [args] as the output of given method [name] to conform the ABI.
   144  // This does not include method ID.
   145  func (abi ABI) PackOutput(name string, args ...interface{}) ([]byte, error) {
   146  	// Fetch the ABI of the requested method
   147  	method, exist := abi.Methods[name]
   148  	if !exist {
   149  		return nil, fmt.Errorf("method '%s' not found", name)
   150  	}
   151  	arguments, err := method.Outputs.Pack(args...)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	return arguments, nil
   156  }
   157  
   158  // getInputs gets input arguments of the given [name] method.
   159  func (abi ABI) getInputs(name string, data []byte) (Arguments, error) {
   160  	// since there can't be naming collisions with contracts and events,
   161  	// we need to decide whether we're calling a method or an event
   162  	var args Arguments
   163  	if method, ok := abi.Methods[name]; ok {
   164  		if len(data)%32 != 0 {
   165  			return nil, fmt.Errorf("abi: improperly formatted input: %s - Bytes: [%+v]", string(data), data)
   166  		}
   167  		args = method.Inputs
   168  	}
   169  	if event, ok := abi.Events[name]; ok {
   170  		args = event.Inputs
   171  	}
   172  	if args == nil {
   173  		return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
   174  	}
   175  	return args, nil
   176  }
   177  
   178  // getArguments gets output arguments of the given [name] method.
   179  func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
   180  	// since there can't be naming collisions with contracts and events,
   181  	// we need to decide whether we're calling a method or an event
   182  	var args Arguments
   183  	if method, ok := abi.Methods[name]; ok {
   184  		if len(data)%32 != 0 {
   185  			return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
   186  		}
   187  		args = method.Outputs
   188  	}
   189  	if event, ok := abi.Events[name]; ok {
   190  		args = event.Inputs
   191  	}
   192  	if args == nil {
   193  		return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
   194  	}
   195  	return args, nil
   196  }
   197  
   198  // UnpackInput unpacks the input according to the ABI specification.
   199  func (abi ABI) UnpackInput(name string, data []byte) ([]interface{}, error) {
   200  	args, err := abi.getInputs(name, data)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	return args.Unpack(data)
   205  }
   206  
   207  // Unpack unpacks the output according to the abi specification.
   208  func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
   209  	args, err := abi.getArguments(name, data)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	return args.Unpack(data)
   214  }
   215  
   216  // UnpackInputIntoInterface unpacks the input in v according to the ABI specification.
   217  // It performs an additional copy. Please only use, if you want to unpack into a
   218  // structure that does not strictly conform to the ABI structure (e.g. has additional arguments)
   219  func (abi ABI) UnpackInputIntoInterface(v interface{}, name string, data []byte) error {
   220  	args, err := abi.getInputs(name, data)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	unpacked, err := args.Unpack(data)
   225  	if err != nil {
   226  		return err
   227  	}
   228  	return args.Copy(v, unpacked)
   229  }
   230  
   231  // UnpackIntoInterface unpacks the output in v according to the abi specification.
   232  // It performs an additional copy. Please only use, if you want to unpack into a
   233  // structure that does not strictly conform to the abi structure (e.g. has additional arguments)
   234  func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
   235  	args, err := abi.getArguments(name, data)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	unpacked, err := args.Unpack(data)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	return args.Copy(v, unpacked)
   244  }
   245  
   246  // UnpackIntoMap unpacks a log into the provided map[string]interface{}.
   247  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
   248  	args, err := abi.getArguments(name, data)
   249  	if err != nil {
   250  		return err
   251  	}
   252  	return args.UnpackIntoMap(v, data)
   253  }
   254  
   255  // UnmarshalJSON implements json.Unmarshaler interface.
   256  func (abi *ABI) UnmarshalJSON(data []byte) error {
   257  	var fields []struct {
   258  		Type    string
   259  		Name    string
   260  		Inputs  []Argument
   261  		Outputs []Argument
   262  
   263  		// Status indicator which can be: "pure", "view",
   264  		// "nonpayable" or "payable".
   265  		StateMutability string
   266  
   267  		// Deprecated Status indicators, but removed in v0.6.0.
   268  		Constant bool // True if function is either pure or view
   269  		Payable  bool // True if function is payable
   270  
   271  		// Event relevant indicator represents the event is
   272  		// declared as anonymous.
   273  		Anonymous bool
   274  	}
   275  	if err := json.Unmarshal(data, &fields); err != nil {
   276  		return err
   277  	}
   278  	abi.Methods = make(map[string]Method)
   279  	abi.Events = make(map[string]Event)
   280  	abi.Errors = make(map[string]Error)
   281  	for _, field := range fields {
   282  		switch field.Type {
   283  		case "constructor":
   284  			abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
   285  		case "function":
   286  			name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
   287  			abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
   288  		case "fallback":
   289  			// New introduced function type in v0.6.0, check more detail
   290  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   291  			if abi.HasFallback() {
   292  				return errors.New("only single fallback is allowed")
   293  			}
   294  			abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
   295  		case "receive":
   296  			// New introduced function type in v0.6.0, check more detail
   297  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   298  			if abi.HasReceive() {
   299  				return errors.New("only single receive is allowed")
   300  			}
   301  			if field.StateMutability != "payable" {
   302  				return errors.New("the statemutability of receive can only be payable")
   303  			}
   304  			abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
   305  		case "event":
   306  			name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
   307  			abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
   308  		case "error":
   309  			// Errors cannot be overloaded or overridden but are inherited,
   310  			// no need to resolve the name conflict here.
   311  			abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
   312  		default:
   313  			return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
   314  		}
   315  	}
   316  	return nil
   317  }
   318  
   319  // MethodById looks up a method by the 4-byte id,
   320  // returns nil if none found.
   321  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   322  	if len(sigdata) < 4 {
   323  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   324  	}
   325  	for _, method := range abi.Methods {
   326  		if bytes.Equal(method.ID, sigdata[:4]) {
   327  			return &method, nil
   328  		}
   329  	}
   330  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   331  }
   332  
   333  // EventByID looks an event up by its topic hash in the
   334  // ABI and returns nil if none found.
   335  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   336  	for _, event := range abi.Events {
   337  		if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
   338  			return &event, nil
   339  		}
   340  	}
   341  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   342  }
   343  
   344  // HasFallback returns an indicator whether a fallback function is included.
   345  func (abi *ABI) HasFallback() bool {
   346  	return abi.Fallback.Type == Fallback
   347  }
   348  
   349  // HasReceive returns an indicator whether a receive function is included.
   350  func (abi *ABI) HasReceive() bool {
   351  	return abi.Receive.Type == Receive
   352  }
   353  
   354  // revertSelector is a special function selector for revert reason unpacking.
   355  var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
   356  
   357  // UnpackRevert resolves the abi-encoded revert reason. According to the solidity
   358  // spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
   359  // the provided revert reason is abi-encoded as if it were a call to a function
   360  // `Error(string)`. So it's a special tool for it.
   361  func UnpackRevert(data []byte) (string, error) {
   362  	if len(data) < 4 {
   363  		return "", errors.New("invalid data for unpacking")
   364  	}
   365  	if !bytes.Equal(data[:4], revertSelector) {
   366  		return "", errors.New("invalid data for unpacking")
   367  	}
   368  	typ, err := NewType("string", "", nil)
   369  	if err != nil {
   370  		return "", err
   371  	}
   372  	unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
   373  	if err != nil {
   374  		return "", err
   375  	}
   376  	return unpacked[0].(string), nil
   377  }