github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/accounts/abi/abi.go (about)

     1  // Copyright 2021 The adkgo Authors
     2  // This file is part of the adkgo library (adapted for adkgo from go--ethereum v1.10.8).
     3  //
     4  // the adkgo 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 adkgo 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 adkgo 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  
    26  	"github.com/aidoskuneen/adk-node/common"
    27  	"github.com/aidoskuneen/adk-node/crypto"
    28  )
    29  
    30  // The ABI holds information about a contract's context and available
    31  // invokable methods. It will allow you to type check function calls and
    32  // packs data accordingly.
    33  type ABI struct {
    34  	Constructor Method
    35  	Methods     map[string]Method
    36  	Events      map[string]Event
    37  
    38  	// Additional "special" functions introduced in solidity v0.6.0.
    39  	// It's separated from the original default fallback. Each contract
    40  	// can only define one fallback and receive function.
    41  	Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
    42  	Receive  Method
    43  }
    44  
    45  // JSON returns a parsed ABI interface and error if it failed.
    46  func JSON(reader io.Reader) (ABI, error) {
    47  	dec := json.NewDecoder(reader)
    48  
    49  	var abi ABI
    50  	if err := dec.Decode(&abi); err != nil {
    51  		return ABI{}, err
    52  	}
    53  	return abi, nil
    54  }
    55  
    56  // Pack the given method name to conform the ABI. Method call's data
    57  // will consist of method_id, args0, arg1, ... argN. Method id consists
    58  // of 4 bytes and arguments are all 32 bytes.
    59  // Method ids are created from the first 4 bytes of the hash of the
    60  // methods string signature. (signature = baz(uint32,string32))
    61  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    62  	// Fetch the ABI of the requested method
    63  	if name == "" {
    64  		// constructor
    65  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		return arguments, nil
    70  	}
    71  	method, exist := abi.Methods[name]
    72  	if !exist {
    73  		return nil, fmt.Errorf("method '%s' not found", name)
    74  	}
    75  	arguments, err := method.Inputs.Pack(args...)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	// Pack up the method ID too if not a constructor and return
    80  	return append(method.ID, arguments...), nil
    81  }
    82  
    83  func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
    84  	// since there can't be naming collisions with contracts and events,
    85  	// we need to decide whether we're calling a method or an event
    86  	var args Arguments
    87  	if method, ok := abi.Methods[name]; ok {
    88  		if len(data)%32 != 0 {
    89  			return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
    90  		}
    91  		args = method.Outputs
    92  	}
    93  	if event, ok := abi.Events[name]; ok {
    94  		args = event.Inputs
    95  	}
    96  	if args == nil {
    97  		return nil, errors.New("abi: could not locate named method or event")
    98  	}
    99  	return args, nil
   100  }
   101  
   102  // Unpack unpacks the output according to the abi specification.
   103  func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
   104  	args, err := abi.getArguments(name, data)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	return args.Unpack(data)
   109  }
   110  
   111  // UnpackIntoInterface unpacks the output in v according to the abi specification.
   112  // It performs an additional copy. Please only use, if you want to unpack into a
   113  // structure that does not strictly conform to the abi structure (e.g. has additional arguments)
   114  func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
   115  	args, err := abi.getArguments(name, data)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	unpacked, err := args.Unpack(data)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	return args.Copy(v, unpacked)
   124  }
   125  
   126  // UnpackIntoMap unpacks a log into the provided map[string]interface{}.
   127  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
   128  	args, err := abi.getArguments(name, data)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	return args.UnpackIntoMap(v, data)
   133  }
   134  
   135  // UnmarshalJSON implements json.Unmarshaler interface.
   136  func (abi *ABI) UnmarshalJSON(data []byte) error {
   137  	var fields []struct {
   138  		Type    string
   139  		Name    string
   140  		Inputs  []Argument
   141  		Outputs []Argument
   142  
   143  		// Status indicator which can be: "pure", "view",
   144  		// "nonpayable" or "payable".
   145  		StateMutability string
   146  
   147  		// Deprecated Status indicators, but removed in v0.6.0.
   148  		Constant bool // True if function is either pure or view
   149  		Payable  bool // True if function is payable
   150  
   151  		// Event relevant indicator represents the event is
   152  		// declared as anonymous.
   153  		Anonymous bool
   154  	}
   155  	if err := json.Unmarshal(data, &fields); err != nil {
   156  		return err
   157  	}
   158  	abi.Methods = make(map[string]Method)
   159  	abi.Events = make(map[string]Event)
   160  	for _, field := range fields {
   161  		switch field.Type {
   162  		case "constructor":
   163  			abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
   164  		case "function":
   165  			name := abi.overloadedMethodName(field.Name)
   166  			abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
   167  		case "fallback":
   168  			// New introduced function type in v0.6.0, check more detail
   169  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   170  			if abi.HasFallback() {
   171  				return errors.New("only single fallback is allowed")
   172  			}
   173  			abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
   174  		case "receive":
   175  			// New introduced function type in v0.6.0, check more detail
   176  			// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
   177  			if abi.HasReceive() {
   178  				return errors.New("only single receive is allowed")
   179  			}
   180  			if field.StateMutability != "payable" {
   181  				return errors.New("the statemutability of receive can only be payable")
   182  			}
   183  			abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
   184  		case "event":
   185  			name := abi.overloadedEventName(field.Name)
   186  			abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
   187  		default:
   188  			return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
   189  		}
   190  	}
   191  	return nil
   192  }
   193  
   194  // overloadedMethodName returns the next available name for a given function.
   195  // Needed since solidity allows for function overload.
   196  //
   197  // e.g. if the abi contains Methods send, send1
   198  // overloadedMethodName would return send2 for input send.
   199  func (abi *ABI) overloadedMethodName(rawName string) string {
   200  	name := rawName
   201  	_, ok := abi.Methods[name]
   202  	for idx := 0; ok; idx++ {
   203  		name = fmt.Sprintf("%s%d", rawName, idx)
   204  		_, ok = abi.Methods[name]
   205  	}
   206  	return name
   207  }
   208  
   209  // overloadedEventName returns the next available name for a given event.
   210  // Needed since solidity allows for event overload.
   211  //
   212  // e.g. if the abi contains events received, received1
   213  // overloadedEventName would return received2 for input received.
   214  func (abi *ABI) overloadedEventName(rawName string) string {
   215  	name := rawName
   216  	_, ok := abi.Events[name]
   217  	for idx := 0; ok; idx++ {
   218  		name = fmt.Sprintf("%s%d", rawName, idx)
   219  		_, ok = abi.Events[name]
   220  	}
   221  	return name
   222  }
   223  
   224  // MethodById looks up a method by the 4-byte id,
   225  // returns nil if none found.
   226  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   227  	if len(sigdata) < 4 {
   228  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   229  	}
   230  	for _, method := range abi.Methods {
   231  		if bytes.Equal(method.ID, sigdata[:4]) {
   232  			return &method, nil
   233  		}
   234  	}
   235  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   236  }
   237  
   238  // EventByID looks an event up by its topic hash in the
   239  // ABI and returns nil if none found.
   240  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   241  	for _, event := range abi.Events {
   242  		if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
   243  			return &event, nil
   244  		}
   245  	}
   246  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   247  }
   248  
   249  // HasFallback returns an indicator whether a fallback function is included.
   250  func (abi *ABI) HasFallback() bool {
   251  	return abi.Fallback.Type == Fallback
   252  }
   253  
   254  // HasReceive returns an indicator whether a receive function is included.
   255  func (abi *ABI) HasReceive() bool {
   256  	return abi.Receive.Type == Receive
   257  }
   258  
   259  // revertSelector is a special function selector for revert reason unpacking.
   260  var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
   261  
   262  // UnpackRevert resolves the abi-encoded revert reason. According to the solidity
   263  // spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
   264  // the provided revert reason is abi-encoded as if it were a call to a function
   265  // `Error(string)`. So it's a special tool for it.
   266  func UnpackRevert(data []byte) (string, error) {
   267  	if len(data) < 4 {
   268  		return "", errors.New("invalid data for unpacking")
   269  	}
   270  	if !bytes.Equal(data[:4], revertSelector) {
   271  		return "", errors.New("invalid data for unpacking")
   272  	}
   273  	typ, _ := NewType("string", "", nil)
   274  	unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
   275  	if err != nil {
   276  		return "", err
   277  	}
   278  	return unpacked[0].(string), nil
   279  }