github.com/lmittmann/w3@v0.20.0/internal/abi/tuple.go (about)

     1  package abi
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  	"reflect"
     8  	"unicode"
     9  
    10  	"github.com/ethereum/go-ethereum/accounts/abi"
    11  	"github.com/ethereum/go-ethereum/common"
    12  )
    13  
    14  var errDuplicateTuple = errors.New("duplicate tuple definition")
    15  
    16  func tupleMap(tuples ...any) (map[string]reflect.Type, error) {
    17  	types := make(map[string]reflect.Type)
    18  	for _, t := range tuples {
    19  		typ := reflect.TypeOf(t)
    20  		if typ.Kind() != reflect.Struct {
    21  			return nil, fmt.Errorf("expected struct, got %s", typ.Kind())
    22  		}
    23  
    24  		if _, ok := types[typ.Name()]; ok {
    25  			return nil, fmt.Errorf("%w: %s", errDuplicateTuple, typ.Name())
    26  		}
    27  		types[typ.Name()] = typ
    28  	}
    29  	return types, nil
    30  }
    31  
    32  func buildTuples(tuples ...any) (map[string]abi.Argument, error) {
    33  	types := make(map[string]abi.Argument)
    34  	for _, t := range tuples {
    35  		typ := reflect.TypeOf(t)
    36  		if typ.Kind() != reflect.Struct {
    37  			return nil, fmt.Errorf("expected struct, got %s", typ.Kind())
    38  		}
    39  
    40  		if _, ok := types[typ.Name()]; ok {
    41  			return nil, fmt.Errorf("%w: %s", errDuplicateTuple, typ.Name())
    42  		}
    43  
    44  		abiTyp, err := typeOf(typ, "")
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  
    49  		arg := abi.Argument{
    50  			Name: typ.Name(),
    51  			Type: *abiTyp,
    52  		}
    53  		types[typ.Name()] = arg
    54  	}
    55  	return types, nil
    56  }
    57  
    58  // typeOfField returns the [abi.Type] of a struct field.
    59  func typeOfField(field reflect.StructField) (*abi.Type, error) {
    60  	const tagKey = "abitype"
    61  
    62  	tag, _ := field.Tag.Lookup(tagKey)
    63  	return typeOf(field.Type, tag) // tag is "" if not set
    64  }
    65  
    66  func typeOf(typ reflect.Type, abiType string) (*abi.Type, error) {
    67  	abiT, isBasicT := basicTypes[typ]
    68  	if !isBasicT {
    69  		switch typ.Kind() {
    70  		case reflect.Slice:
    71  			elemAibT, err := typeOf(typ.Elem(), abiType)
    72  			if err != nil {
    73  				return nil, err
    74  			}
    75  			return &abi.Type{
    76  				T:    abi.SliceTy,
    77  				Elem: elemAibT,
    78  			}, nil
    79  		case reflect.Array:
    80  			elemAibT, err := typeOf(typ.Elem(), abiType)
    81  			if err != nil {
    82  				return nil, err
    83  			}
    84  			return &abi.Type{
    85  				T:    abi.ArrayTy,
    86  				Elem: elemAibT,
    87  				Size: typ.Len(),
    88  			}, nil
    89  		case reflect.Struct:
    90  			num := typ.NumField()
    91  			elems := make([]*abi.Type, num)
    92  			rawNames := make([]string, num)
    93  			for i := range num {
    94  				f := typ.Field(i)
    95  				elemType, err := typeOfField(f)
    96  				if err != nil {
    97  					return nil, err
    98  				}
    99  				elems[i] = elemType
   100  				rawNames[i] = toCamelCase(f.Name)
   101  			}
   102  			return &abi.Type{
   103  				T:             abi.TupleTy,
   104  				TupleElems:    elems,
   105  				TupleRawName:  typ.Name(),
   106  				TupleRawNames: rawNames,
   107  				TupleType:     typ,
   108  			}, nil
   109  		}
   110  		return nil, fmt.Errorf("unknown type %q", typ)
   111  	}
   112  
   113  	if abiType == "" {
   114  		// if no abiType is specified, return the basic type directly.
   115  		return &abiT, nil
   116  	}
   117  
   118  	abiT, ok := types[abiType]
   119  	if !ok {
   120  		return nil, fmt.Errorf("unknown abi type %q", abiType)
   121  	}
   122  	if abiT.GetType() != typ && !(typ == reflect.TypeFor[*big.Int]() &&
   123  		(abiT.GetType().Kind() == reflect.Int16 ||
   124  			abiT.GetType().Kind() == reflect.Int32 ||
   125  			abiT.GetType().Kind() == reflect.Int64 ||
   126  			abiT.GetType().Kind() == reflect.Uint16 ||
   127  			abiT.GetType().Kind() == reflect.Uint32 ||
   128  			abiT.GetType().Kind() == reflect.Uint64)) {
   129  		return nil, fmt.Errorf("tagged type %q does not match type %v", abiType, typ)
   130  	}
   131  	return &abiT, nil
   132  }
   133  
   134  var basicTypes = map[reflect.Type]abi.Type{
   135  	reflect.TypeFor[bool]():           {T: abi.BoolTy},
   136  	reflect.TypeFor[byte]():           {T: abi.UintTy, Size: 8},
   137  	reflect.TypeFor[uint8]():          {T: abi.UintTy, Size: 8},
   138  	reflect.TypeFor[uint16]():         {T: abi.UintTy, Size: 16},
   139  	reflect.TypeFor[uint32]():         {T: abi.UintTy, Size: 32},
   140  	reflect.TypeFor[uint64]():         {T: abi.UintTy, Size: 64},
   141  	reflect.TypeFor[int8]():           {T: abi.IntTy, Size: 8},
   142  	reflect.TypeFor[int16]():          {T: abi.IntTy, Size: 16},
   143  	reflect.TypeFor[int32]():          {T: abi.IntTy, Size: 32},
   144  	reflect.TypeFor[int64]():          {T: abi.IntTy, Size: 64},
   145  	reflect.TypeFor[[1]byte]():        {T: abi.FixedBytesTy, Size: 1},
   146  	reflect.TypeFor[[2]byte]():        {T: abi.FixedBytesTy, Size: 2},
   147  	reflect.TypeFor[[3]byte]():        {T: abi.FixedBytesTy, Size: 3},
   148  	reflect.TypeFor[[4]byte]():        {T: abi.FixedBytesTy, Size: 4},
   149  	reflect.TypeFor[[5]byte]():        {T: abi.FixedBytesTy, Size: 5},
   150  	reflect.TypeFor[[6]byte]():        {T: abi.FixedBytesTy, Size: 6},
   151  	reflect.TypeFor[[7]byte]():        {T: abi.FixedBytesTy, Size: 7},
   152  	reflect.TypeFor[[8]byte]():        {T: abi.FixedBytesTy, Size: 8},
   153  	reflect.TypeFor[[9]byte]():        {T: abi.FixedBytesTy, Size: 9},
   154  	reflect.TypeFor[[10]byte]():       {T: abi.FixedBytesTy, Size: 10},
   155  	reflect.TypeFor[[11]byte]():       {T: abi.FixedBytesTy, Size: 11},
   156  	reflect.TypeFor[[12]byte]():       {T: abi.FixedBytesTy, Size: 12},
   157  	reflect.TypeFor[[13]byte]():       {T: abi.FixedBytesTy, Size: 13},
   158  	reflect.TypeFor[[14]byte]():       {T: abi.FixedBytesTy, Size: 14},
   159  	reflect.TypeFor[[15]byte]():       {T: abi.FixedBytesTy, Size: 15},
   160  	reflect.TypeFor[[16]byte]():       {T: abi.FixedBytesTy, Size: 16},
   161  	reflect.TypeFor[[17]byte]():       {T: abi.FixedBytesTy, Size: 17},
   162  	reflect.TypeFor[[18]byte]():       {T: abi.FixedBytesTy, Size: 18},
   163  	reflect.TypeFor[[19]byte]():       {T: abi.FixedBytesTy, Size: 19},
   164  	reflect.TypeFor[[20]byte]():       {T: abi.FixedBytesTy, Size: 20},
   165  	reflect.TypeFor[[21]byte]():       {T: abi.FixedBytesTy, Size: 21},
   166  	reflect.TypeFor[[22]byte]():       {T: abi.FixedBytesTy, Size: 22},
   167  	reflect.TypeFor[[23]byte]():       {T: abi.FixedBytesTy, Size: 23},
   168  	reflect.TypeFor[[24]byte]():       {T: abi.FixedBytesTy, Size: 24},
   169  	reflect.TypeFor[[25]byte]():       {T: abi.FixedBytesTy, Size: 25},
   170  	reflect.TypeFor[[26]byte]():       {T: abi.FixedBytesTy, Size: 26},
   171  	reflect.TypeFor[[27]byte]():       {T: abi.FixedBytesTy, Size: 27},
   172  	reflect.TypeFor[[28]byte]():       {T: abi.FixedBytesTy, Size: 28},
   173  	reflect.TypeFor[[29]byte]():       {T: abi.FixedBytesTy, Size: 29},
   174  	reflect.TypeFor[[30]byte]():       {T: abi.FixedBytesTy, Size: 30},
   175  	reflect.TypeFor[[31]byte]():       {T: abi.FixedBytesTy, Size: 31},
   176  	reflect.TypeFor[[32]byte]():       {T: abi.FixedBytesTy, Size: 32},
   177  	reflect.TypeFor[common.Address](): {T: abi.AddressTy, Size: 20},
   178  	reflect.TypeFor[common.Hash]():    {T: abi.FixedBytesTy, Size: 32},
   179  	reflect.TypeFor[string]():         {T: abi.StringTy},
   180  	reflect.TypeFor[[]byte]():         {T: abi.BytesTy},
   181  	reflect.TypeFor[*big.Int]():       {T: abi.UintTy, Size: 256},
   182  }
   183  
   184  func toCamelCase(s string) string {
   185  	if len(s) == 0 {
   186  		return s
   187  	}
   188  
   189  	r := []rune(s)
   190  
   191  	var prevUp bool
   192  	for i := range len(r) {
   193  		if i == 0 && unicode.IsUpper(r[i]) {
   194  			prevUp = true
   195  			r[i] = unicode.ToLower(r[i])
   196  		} else if unicode.IsUpper(r[i]) {
   197  			if prevUp {
   198  				r[i] = unicode.ToLower(r[i])
   199  			}
   200  			prevUp = true
   201  		} else {
   202  			prevUp = false
   203  		}
   204  	}
   205  
   206  	return string(r)
   207  }