github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/accounts/abi/abi.go (about)

     1  package abi
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  
    10  	"github.com/neatlab/neatio/utilities/common"
    11  	"github.com/neatlab/neatio/utilities/crypto"
    12  )
    13  
    14  type ABI struct {
    15  	Constructor Method
    16  	Methods     map[string]Method
    17  	Events      map[string]Event
    18  }
    19  
    20  func JSON(reader io.Reader) (ABI, error) {
    21  	dec := json.NewDecoder(reader)
    22  
    23  	var abi ABI
    24  	if err := dec.Decode(&abi); err != nil {
    25  		return ABI{}, err
    26  	}
    27  
    28  	return abi, nil
    29  }
    30  
    31  func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
    32  
    33  	if name == "" {
    34  
    35  		arguments, err := abi.Constructor.Inputs.Pack(args...)
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  		return arguments, nil
    40  
    41  	}
    42  	method, exist := abi.Methods[name]
    43  	if !exist {
    44  		return nil, fmt.Errorf("method '%s' not found", name)
    45  	}
    46  
    47  	arguments, err := method.Inputs.Pack(args...)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	return append(method.ID(), arguments...), nil
    53  }
    54  
    55  func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
    56  
    57  	if method, ok := abi.Methods[name]; ok {
    58  		if len(data)%32 != 0 {
    59  			return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
    60  		}
    61  		return method.Outputs.Unpack(v, data)
    62  	}
    63  	if event, ok := abi.Events[name]; ok {
    64  		return event.Inputs.Unpack(v, data)
    65  	}
    66  	return fmt.Errorf("abi: could not locate named method or event")
    67  }
    68  
    69  func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
    70  
    71  	if method, ok := abi.Methods[name]; ok {
    72  		if len(data)%32 != 0 {
    73  			return fmt.Errorf("abi: improperly formatted output")
    74  		}
    75  		return method.Outputs.UnpackIntoMap(v, data)
    76  	}
    77  	if event, ok := abi.Events[name]; ok {
    78  		return event.Inputs.UnpackIntoMap(v, data)
    79  	}
    80  	return fmt.Errorf("abi: could not locate named method or event")
    81  }
    82  
    83  func (abi ABI) UnpackMethodInputs(v interface{}, name string, input []byte) (err error) {
    84  	if len(input) == 0 {
    85  		return fmt.Errorf("abi: unmarshalling empty output")
    86  	}
    87  	if method, ok := abi.Methods[name]; ok {
    88  		return method.Inputs.Unpack(v, input)
    89  	}
    90  	return fmt.Errorf("abi: could not locate named method or event")
    91  }
    92  
    93  func (abi *ABI) UnmarshalJSON(data []byte) error {
    94  	var fields []struct {
    95  		Type            string
    96  		Name            string
    97  		Constant        bool
    98  		StateMutability string
    99  		Anonymous       bool
   100  		Inputs          []Argument
   101  		Outputs         []Argument
   102  	}
   103  
   104  	if err := json.Unmarshal(data, &fields); err != nil {
   105  		return err
   106  	}
   107  
   108  	abi.Methods = make(map[string]Method)
   109  	abi.Events = make(map[string]Event)
   110  	for _, field := range fields {
   111  		switch field.Type {
   112  		case "constructor":
   113  			abi.Constructor = Method{
   114  				Inputs: field.Inputs,
   115  			}
   116  
   117  		case "function", "":
   118  			name := field.Name
   119  			_, ok := abi.Methods[name]
   120  			for idx := 0; ok; idx++ {
   121  				name = fmt.Sprintf("%s%d", field.Name, idx)
   122  				_, ok = abi.Methods[name]
   123  			}
   124  			isConst := field.Constant || field.StateMutability == "pure" || field.StateMutability == "view"
   125  			abi.Methods[name] = Method{
   126  				Name:    name,
   127  				RawName: field.Name,
   128  				Const:   isConst,
   129  				Inputs:  field.Inputs,
   130  				Outputs: field.Outputs,
   131  			}
   132  		case "event":
   133  			name := field.Name
   134  			_, ok := abi.Events[name]
   135  			for idx := 0; ok; idx++ {
   136  				name = fmt.Sprintf("%s%d", field.Name, idx)
   137  				_, ok = abi.Events[name]
   138  			}
   139  			abi.Events[name] = Event{
   140  				Name:      name,
   141  				RawName:   field.Name,
   142  				Anonymous: field.Anonymous,
   143  				Inputs:    field.Inputs,
   144  			}
   145  		}
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
   152  	if len(sigdata) < 4 {
   153  		return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
   154  	}
   155  	for _, method := range abi.Methods {
   156  		if bytes.Equal(method.ID(), sigdata[:4]) {
   157  			return &method, nil
   158  		}
   159  	}
   160  	return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
   161  }
   162  
   163  func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
   164  	for _, event := range abi.Events {
   165  		if bytes.Equal(event.ID().Bytes(), topic.Bytes()) {
   166  			return &event, nil
   167  		}
   168  	}
   169  	return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
   170  }
   171  
   172  var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
   173  
   174  func UnpackRevert(data []byte) (string, error) {
   175  	if len(data) < 4 {
   176  		return "", fmt.Errorf("invalid data for unpacking")
   177  	}
   178  	if !bytes.Equal(data[:4], revertSelector) {
   179  		return "", fmt.Errorf("invalid data for unpacking")
   180  	}
   181  	typ, _ := NewType("string", "", nil)
   182  	unpacked, err := (Arguments{{Type: typ}}).UnpackForRevert(reflect.New(reflect.TypeOf(typ)).Interface(), data[4:])
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  	return unpacked[0].(string), nil
   187  }