github.com/diadata-org/diadata@v1.4.593/pkg/utils/typeddata.go (about)

     1  package utils
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"math/big"
     8  	"reflect"
     9  	"regexp"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"unicode"
    14  	"unicode/utf8"
    15  
    16  	"github.com/ethereum/go-ethereum/common"
    17  	"github.com/ethereum/go-ethereum/common/hexutil"
    18  	"github.com/ethereum/go-ethereum/common/math"
    19  	"github.com/ethereum/go-ethereum/crypto"
    20  )
    21  
    22  var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`)
    23  
    24  type TypedData struct {
    25  	Types       Types            `json:"types"`
    26  	PrimaryType string           `json:"primaryType"`
    27  	Domain      TypedDataDomain  `json:"domain"`
    28  	Message     TypedDataMessage `json:"message"`
    29  }
    30  
    31  // Type is the inner type of an EIP-712 message
    32  type Type struct {
    33  	Name string `json:"name"`
    34  	Type string `json:"type"`
    35  }
    36  
    37  func (t *Type) isArray() bool {
    38  	return strings.HasSuffix(t.Type, "[]")
    39  }
    40  
    41  // typeName returns the canonical name of the type. If the type is 'Person[]', then
    42  // this method returns 'Person'
    43  func (t *Type) typeName() string {
    44  	if strings.HasSuffix(t.Type, "[]") {
    45  		return strings.TrimSuffix(t.Type, "[]")
    46  	}
    47  	return t.Type
    48  }
    49  
    50  func (t *Type) isReferenceType() bool {
    51  	if len(t.Type) == 0 {
    52  		return false
    53  	}
    54  	// Reference types must have a leading uppercase character
    55  	r, _ := utf8.DecodeRuneInString(t.Type)
    56  	return unicode.IsUpper(r)
    57  }
    58  
    59  type Types map[string][]Type
    60  
    61  type TypePriority struct {
    62  	Type  string
    63  	Value uint
    64  }
    65  
    66  type TypedDataMessage = map[string]interface{}
    67  
    68  // TypedDataDomain represents the domain part of an EIP-712 message.
    69  type TypedDataDomain struct {
    70  	Name              string                `json:"name"`
    71  	Version           string                `json:"version"`
    72  	ChainId           *math.HexOrDecimal256 `json:"chainId"`
    73  	VerifyingContract string                `json:"verifyingContract"`
    74  	Salt              string                `json:"salt"`
    75  }
    76  
    77  // TypedDataAndHash is a helper function that calculates a hash for typed data conforming to EIP-712.
    78  // This hash can then be safely used to calculate a signature.
    79  //
    80  // See https://eips.ethereum.org/EIPS/eip-712 for the full specification.
    81  //
    82  // This gives context to the signed typed data and prevents signing of transactions.
    83  func TypedDataAndHash(typedData TypedData) ([]byte, string, error) {
    84  	domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
    85  	if err != nil {
    86  		return nil, "", err
    87  	}
    88  	typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
    89  	if err != nil {
    90  		return nil, "", err
    91  	}
    92  	rawData := fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))
    93  	return crypto.Keccak256([]byte(rawData)), rawData, nil
    94  }
    95  
    96  // HashStruct generates a keccak256 hash of the encoding of the provided data
    97  func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) {
    98  	encodedData, err := typedData.EncodeData(primaryType, data, 1)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return crypto.Keccak256(encodedData), nil
   103  }
   104  
   105  // Dependencies returns an array of custom types ordered by their hierarchical reference tree
   106  func (typedData *TypedData) Dependencies(primaryType string, found []string) []string {
   107  	primaryType = strings.TrimSuffix(primaryType, "[]")
   108  	includes := func(arr []string, str string) bool {
   109  		for _, obj := range arr {
   110  			if obj == str {
   111  				return true
   112  			}
   113  		}
   114  		return false
   115  	}
   116  
   117  	if includes(found, primaryType) {
   118  		return found
   119  	}
   120  	if typedData.Types[primaryType] == nil {
   121  		return found
   122  	}
   123  	found = append(found, primaryType)
   124  	for _, field := range typedData.Types[primaryType] {
   125  		for _, dep := range typedData.Dependencies(field.Type, found) {
   126  			if !includes(found, dep) {
   127  				found = append(found, dep)
   128  			}
   129  		}
   130  	}
   131  	return found
   132  }
   133  
   134  // EncodeType generates the following encoding:
   135  // `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
   136  //
   137  // each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name
   138  func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes {
   139  	// Get dependencies primary first, then alphabetical
   140  	deps := typedData.Dependencies(primaryType, []string{})
   141  	if len(deps) > 0 {
   142  		slicedDeps := deps[1:]
   143  		sort.Strings(slicedDeps)
   144  		deps = append([]string{primaryType}, slicedDeps...)
   145  	}
   146  
   147  	// Format as a string with fields
   148  	var buffer bytes.Buffer
   149  	for _, dep := range deps {
   150  		buffer.WriteString(dep)
   151  		buffer.WriteString("(")
   152  		for _, obj := range typedData.Types[dep] {
   153  			buffer.WriteString(obj.Type)
   154  			buffer.WriteString(" ")
   155  			buffer.WriteString(obj.Name)
   156  			buffer.WriteString(",")
   157  		}
   158  		buffer.Truncate(buffer.Len() - 1)
   159  		buffer.WriteString(")")
   160  	}
   161  	return buffer.Bytes()
   162  }
   163  
   164  // TypeHash creates the keccak256 hash  of the data
   165  func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes {
   166  	return crypto.Keccak256(typedData.EncodeType(primaryType))
   167  }
   168  
   169  // EncodeData generates the following encoding:
   170  // `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
   171  //
   172  // each encoded member is 32-byte long
   173  func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) {
   174  	if err := typedData.validate(); err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	buffer := bytes.Buffer{}
   179  
   180  	// Verify extra data
   181  	if exp, got := len(typedData.Types[primaryType]), len(data); exp < got {
   182  		return nil, fmt.Errorf("there is extra data provided in the message (%d < %d)", exp, got)
   183  	}
   184  
   185  	// Add typehash
   186  	buffer.Write(typedData.TypeHash(primaryType))
   187  
   188  	// Add field contents. Structs and arrays have special handlers.
   189  	for _, field := range typedData.Types[primaryType] {
   190  		encType := field.Type
   191  		encValue := data[field.Name]
   192  		if encType[len(encType)-1:] == "]" {
   193  			arrayValue, ok := encValue.([]interface{})
   194  			if !ok {
   195  				return nil, dataMismatchError(encType, encValue)
   196  			}
   197  
   198  			arrayBuffer := bytes.Buffer{}
   199  			parsedType := strings.Split(encType, "[")[0]
   200  			for _, item := range arrayValue {
   201  				if typedData.Types[parsedType] != nil {
   202  					mapValue, ok := item.(map[string]interface{})
   203  					if !ok {
   204  						return nil, dataMismatchError(parsedType, item)
   205  					}
   206  					encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1)
   207  					if err != nil {
   208  						return nil, err
   209  					}
   210  					arrayBuffer.Write(crypto.Keccak256(encodedData))
   211  				} else {
   212  					bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth)
   213  					if err != nil {
   214  						return nil, err
   215  					}
   216  					arrayBuffer.Write(bytesValue)
   217  				}
   218  			}
   219  
   220  			buffer.Write(crypto.Keccak256(arrayBuffer.Bytes()))
   221  		} else if typedData.Types[field.Type] != nil {
   222  			mapValue, ok := encValue.(map[string]interface{})
   223  			if !ok {
   224  				return nil, dataMismatchError(encType, encValue)
   225  			}
   226  			encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1)
   227  			if err != nil {
   228  				return nil, err
   229  			}
   230  			buffer.Write(crypto.Keccak256(encodedData))
   231  		} else {
   232  			byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth)
   233  			if err != nil {
   234  				return nil, err
   235  			}
   236  			buffer.Write(byteValue)
   237  		}
   238  	}
   239  	return buffer.Bytes(), nil
   240  }
   241  
   242  // Attempt to parse bytes in different formats: byte array, hex string, hexutil.Bytes.
   243  func parseBytes(encType interface{}) ([]byte, bool) {
   244  	switch v := encType.(type) {
   245  	case []byte:
   246  		return v, true
   247  	case hexutil.Bytes:
   248  		return v, true
   249  	case string:
   250  		bytes, err := hexutil.Decode(v)
   251  		if err != nil {
   252  			return nil, false
   253  		}
   254  		return bytes, true
   255  	default:
   256  		return nil, false
   257  	}
   258  }
   259  
   260  func parseInteger(encType string, encValue interface{}) (*big.Int, error) {
   261  	var (
   262  		length int
   263  		signed = strings.HasPrefix(encType, "int")
   264  		b      *big.Int
   265  	)
   266  	if encType == "int" || encType == "uint" {
   267  		length = 256
   268  	} else {
   269  		lengthStr := ""
   270  		if strings.HasPrefix(encType, "uint") {
   271  			lengthStr = strings.TrimPrefix(encType, "uint")
   272  		} else {
   273  			lengthStr = strings.TrimPrefix(encType, "int")
   274  		}
   275  		atoiSize, err := strconv.Atoi(lengthStr)
   276  		if err != nil {
   277  			return nil, fmt.Errorf("invalid size on integer: %v", lengthStr)
   278  		}
   279  		length = atoiSize
   280  	}
   281  	switch v := encValue.(type) {
   282  	case *math.HexOrDecimal256:
   283  		b = (*big.Int)(v)
   284  	case string:
   285  		var hexIntValue math.HexOrDecimal256
   286  		if err := hexIntValue.UnmarshalText([]byte(v)); err != nil {
   287  			return nil, err
   288  		}
   289  		b = (*big.Int)(&hexIntValue)
   290  	case float64:
   291  		// JSON parses non-strings as float64. Fail if we cannot
   292  		// convert it losslessly
   293  		if float64(int64(v)) == v {
   294  			b = big.NewInt(int64(v))
   295  		} else {
   296  			return nil, fmt.Errorf("invalid float value %v for type %v", v, encType)
   297  		}
   298  	}
   299  	if b == nil {
   300  		return nil, fmt.Errorf("invalid integer value %v/%v for type %v", encValue, reflect.TypeOf(encValue), encType)
   301  	}
   302  	if b.BitLen() > length {
   303  		return nil, fmt.Errorf("integer larger than '%v'", encType)
   304  	}
   305  	if !signed && b.Sign() == -1 {
   306  		return nil, fmt.Errorf("invalid negative value for unsigned type %v", encType)
   307  	}
   308  	return b, nil
   309  }
   310  
   311  // EncodePrimitiveValue deals with the primitive values found
   312  // while searching through the typed data
   313  func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) {
   314  	switch encType {
   315  	case "address":
   316  		stringValue, ok := encValue.(string)
   317  		if !ok || !common.IsHexAddress(stringValue) {
   318  			return nil, dataMismatchError(encType, encValue)
   319  		}
   320  		retval := make([]byte, 32)
   321  		copy(retval[12:], common.HexToAddress(stringValue).Bytes())
   322  		return retval, nil
   323  	case "bool":
   324  		boolValue, ok := encValue.(bool)
   325  		if !ok {
   326  			return nil, dataMismatchError(encType, encValue)
   327  		}
   328  		if boolValue {
   329  			return math.PaddedBigBytes(common.Big1, 32), nil
   330  		}
   331  		return math.PaddedBigBytes(common.Big0, 32), nil
   332  	case "string":
   333  		strVal, ok := encValue.(string)
   334  		if !ok {
   335  			return nil, dataMismatchError(encType, encValue)
   336  		}
   337  		return crypto.Keccak256([]byte(strVal)), nil
   338  	case "bytes":
   339  		bytesValue, ok := parseBytes(encValue)
   340  		if !ok {
   341  			return nil, dataMismatchError(encType, encValue)
   342  		}
   343  		return crypto.Keccak256(bytesValue), nil
   344  	}
   345  	if strings.HasPrefix(encType, "bytes") {
   346  		lengthStr := strings.TrimPrefix(encType, "bytes")
   347  		length, err := strconv.Atoi(lengthStr)
   348  		if err != nil {
   349  			return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr)
   350  		}
   351  		if length < 0 || length > 32 {
   352  			return nil, fmt.Errorf("invalid size on bytes: %d", length)
   353  		}
   354  		if byteValue, ok := parseBytes(encValue); !ok || len(byteValue) != length {
   355  			return nil, dataMismatchError(encType, encValue)
   356  		} else {
   357  			// Right-pad the bits
   358  			dst := make([]byte, 32)
   359  			copy(dst, byteValue)
   360  			return dst, nil
   361  		}
   362  	}
   363  	if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
   364  		b, err := parseInteger(encType, encValue)
   365  		if err != nil {
   366  			return nil, err
   367  		}
   368  		return math.U256Bytes(b), nil
   369  	}
   370  	return nil, fmt.Errorf("unrecognized type '%s'", encType)
   371  }
   372  
   373  // dataMismatchError generates an error for a mismatch between
   374  // the provided type and data
   375  func dataMismatchError(encType string, encValue interface{}) error {
   376  	return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType)
   377  }
   378  
   379  // validate makes sure the types are sound
   380  func (typedData *TypedData) validate() error {
   381  	if err := typedData.Types.validate(); err != nil {
   382  		return err
   383  	}
   384  	if err := typedData.Domain.validate(); err != nil {
   385  		return err
   386  	}
   387  	return nil
   388  }
   389  
   390  // Map generates a map version of the typed data
   391  func (typedData *TypedData) Map() map[string]interface{} {
   392  	dataMap := map[string]interface{}{
   393  		"types":       typedData.Types,
   394  		"domain":      typedData.Domain.Map(),
   395  		"primaryType": typedData.PrimaryType,
   396  		"message":     typedData.Message,
   397  	}
   398  	return dataMap
   399  }
   400  
   401  // Format returns a representation of typedData, which can be easily displayed by a user-interface
   402  // without in-depth knowledge about 712 rules
   403  func (typedData *TypedData) Format() ([]*NameValueType, error) {
   404  	domain, err := typedData.formatData("EIP712Domain", typedData.Domain.Map())
   405  	if err != nil {
   406  		return nil, err
   407  	}
   408  	ptype, err := typedData.formatData(typedData.PrimaryType, typedData.Message)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	var nvts []*NameValueType
   413  	nvts = append(nvts, &NameValueType{
   414  		Name:  "EIP712Domain",
   415  		Value: domain,
   416  		Typ:   "domain",
   417  	})
   418  	nvts = append(nvts, &NameValueType{
   419  		Name:  typedData.PrimaryType,
   420  		Value: ptype,
   421  		Typ:   "primary type",
   422  	})
   423  	return nvts, nil
   424  }
   425  
   426  func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) ([]*NameValueType, error) {
   427  	var output []*NameValueType
   428  
   429  	// Add field contents. Structs and arrays have special handlers.
   430  	for _, field := range typedData.Types[primaryType] {
   431  		encName := field.Name
   432  		encValue := data[encName]
   433  		item := &NameValueType{
   434  			Name: encName,
   435  			Typ:  field.Type,
   436  		}
   437  		if field.isArray() {
   438  			arrayValue, _ := encValue.([]interface{})
   439  			parsedType := field.typeName()
   440  			for _, v := range arrayValue {
   441  				if typedData.Types[parsedType] != nil {
   442  					mapValue, _ := v.(map[string]interface{})
   443  					mapOutput, err := typedData.formatData(parsedType, mapValue)
   444  					if err != nil {
   445  						return nil, err
   446  					}
   447  					item.Value = mapOutput
   448  				} else {
   449  					primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
   450  					if err != nil {
   451  						return nil, err
   452  					}
   453  					item.Value = primitiveOutput
   454  				}
   455  			}
   456  		} else if typedData.Types[field.Type] != nil {
   457  			if mapValue, ok := encValue.(map[string]interface{}); ok {
   458  				mapOutput, err := typedData.formatData(field.Type, mapValue)
   459  				if err != nil {
   460  					return nil, err
   461  				}
   462  				item.Value = mapOutput
   463  			} else {
   464  				item.Value = "<nil>"
   465  			}
   466  		} else {
   467  			primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
   468  			if err != nil {
   469  				return nil, err
   470  			}
   471  			item.Value = primitiveOutput
   472  		}
   473  		output = append(output, item)
   474  	}
   475  	return output, nil
   476  }
   477  
   478  func formatPrimitiveValue(encType string, encValue interface{}) (string, error) {
   479  	switch encType {
   480  	case "address":
   481  		if stringValue, ok := encValue.(string); !ok {
   482  			return "", fmt.Errorf("could not format value %v as address", encValue)
   483  		} else {
   484  			return common.HexToAddress(stringValue).String(), nil
   485  		}
   486  	case "bool":
   487  		if boolValue, ok := encValue.(bool); !ok {
   488  			return "", fmt.Errorf("could not format value %v as bool", encValue)
   489  		} else {
   490  			return fmt.Sprintf("%t", boolValue), nil
   491  		}
   492  	case "bytes", "string":
   493  		return fmt.Sprintf("%s", encValue), nil
   494  	}
   495  	if strings.HasPrefix(encType, "bytes") {
   496  		return fmt.Sprintf("%s", encValue), nil
   497  	}
   498  	if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
   499  		if b, err := parseInteger(encType, encValue); err != nil {
   500  			return "", err
   501  		} else {
   502  			return fmt.Sprintf("%d (%#x)", b, b), nil
   503  		}
   504  	}
   505  	return "", fmt.Errorf("unhandled type %v", encType)
   506  }
   507  
   508  // Validate checks if the types object is conformant to the specs
   509  func (t Types) validate() error {
   510  	for typeKey, typeArr := range t {
   511  		if len(typeKey) == 0 {
   512  			return fmt.Errorf("empty type key")
   513  		}
   514  		for i, typeObj := range typeArr {
   515  			if len(typeObj.Type) == 0 {
   516  				return fmt.Errorf("type %q:%d: empty Type", typeKey, i)
   517  			}
   518  			if len(typeObj.Name) == 0 {
   519  				return fmt.Errorf("type %q:%d: empty Name", typeKey, i)
   520  			}
   521  			if typeKey == typeObj.Type {
   522  				return fmt.Errorf("type %q cannot reference itself", typeObj.Type)
   523  			}
   524  			if typeObj.isReferenceType() {
   525  				if _, exist := t[typeObj.typeName()]; !exist {
   526  					return fmt.Errorf("reference type %q is undefined", typeObj.Type)
   527  				}
   528  				if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
   529  					return fmt.Errorf("unknown reference type %q", typeObj.Type)
   530  				}
   531  			} else if !isPrimitiveTypeValid(typeObj.Type) {
   532  				return fmt.Errorf("unknown type %q", typeObj.Type)
   533  			}
   534  		}
   535  	}
   536  	return nil
   537  }
   538  
   539  // Checks if the primitive value is valid
   540  func isPrimitiveTypeValid(primitiveType string) bool {
   541  	if primitiveType == "address" ||
   542  		primitiveType == "address[]" ||
   543  		primitiveType == "bool" ||
   544  		primitiveType == "bool[]" ||
   545  		primitiveType == "string" ||
   546  		primitiveType == "string[]" {
   547  		return true
   548  	}
   549  	if primitiveType == "bytes" ||
   550  		primitiveType == "bytes[]" ||
   551  		primitiveType == "bytes1" ||
   552  		primitiveType == "bytes1[]" ||
   553  		primitiveType == "bytes2" ||
   554  		primitiveType == "bytes2[]" ||
   555  		primitiveType == "bytes3" ||
   556  		primitiveType == "bytes3[]" ||
   557  		primitiveType == "bytes4" ||
   558  		primitiveType == "bytes4[]" ||
   559  		primitiveType == "bytes5" ||
   560  		primitiveType == "bytes5[]" ||
   561  		primitiveType == "bytes6" ||
   562  		primitiveType == "bytes6[]" ||
   563  		primitiveType == "bytes7" ||
   564  		primitiveType == "bytes7[]" ||
   565  		primitiveType == "bytes8" ||
   566  		primitiveType == "bytes8[]" ||
   567  		primitiveType == "bytes9" ||
   568  		primitiveType == "bytes9[]" ||
   569  		primitiveType == "bytes10" ||
   570  		primitiveType == "bytes10[]" ||
   571  		primitiveType == "bytes11" ||
   572  		primitiveType == "bytes11[]" ||
   573  		primitiveType == "bytes12" ||
   574  		primitiveType == "bytes12[]" ||
   575  		primitiveType == "bytes13" ||
   576  		primitiveType == "bytes13[]" ||
   577  		primitiveType == "bytes14" ||
   578  		primitiveType == "bytes14[]" ||
   579  		primitiveType == "bytes15" ||
   580  		primitiveType == "bytes15[]" ||
   581  		primitiveType == "bytes16" ||
   582  		primitiveType == "bytes16[]" ||
   583  		primitiveType == "bytes17" ||
   584  		primitiveType == "bytes17[]" ||
   585  		primitiveType == "bytes18" ||
   586  		primitiveType == "bytes18[]" ||
   587  		primitiveType == "bytes19" ||
   588  		primitiveType == "bytes19[]" ||
   589  		primitiveType == "bytes20" ||
   590  		primitiveType == "bytes20[]" ||
   591  		primitiveType == "bytes21" ||
   592  		primitiveType == "bytes21[]" ||
   593  		primitiveType == "bytes22" ||
   594  		primitiveType == "bytes22[]" ||
   595  		primitiveType == "bytes23" ||
   596  		primitiveType == "bytes23[]" ||
   597  		primitiveType == "bytes24" ||
   598  		primitiveType == "bytes24[]" ||
   599  		primitiveType == "bytes25" ||
   600  		primitiveType == "bytes25[]" ||
   601  		primitiveType == "bytes26" ||
   602  		primitiveType == "bytes26[]" ||
   603  		primitiveType == "bytes27" ||
   604  		primitiveType == "bytes27[]" ||
   605  		primitiveType == "bytes28" ||
   606  		primitiveType == "bytes28[]" ||
   607  		primitiveType == "bytes29" ||
   608  		primitiveType == "bytes29[]" ||
   609  		primitiveType == "bytes30" ||
   610  		primitiveType == "bytes30[]" ||
   611  		primitiveType == "bytes31" ||
   612  		primitiveType == "bytes31[]" ||
   613  		primitiveType == "bytes32" ||
   614  		primitiveType == "bytes32[]" {
   615  		return true
   616  	}
   617  	if primitiveType == "int" ||
   618  		primitiveType == "int[]" ||
   619  		primitiveType == "int8" ||
   620  		primitiveType == "int8[]" ||
   621  		primitiveType == "int16" ||
   622  		primitiveType == "int16[]" ||
   623  		primitiveType == "int32" ||
   624  		primitiveType == "int32[]" ||
   625  		primitiveType == "int64" ||
   626  		primitiveType == "int64[]" ||
   627  		primitiveType == "int96" ||
   628  		primitiveType == "int96[]" ||
   629  		primitiveType == "int128" ||
   630  		primitiveType == "int128[]" ||
   631  		primitiveType == "int256" ||
   632  		primitiveType == "int256[]" {
   633  		return true
   634  	}
   635  	if primitiveType == "uint" ||
   636  		primitiveType == "uint[]" ||
   637  		primitiveType == "uint8" ||
   638  		primitiveType == "uint8[]" ||
   639  		primitiveType == "uint16" ||
   640  		primitiveType == "uint16[]" ||
   641  		primitiveType == "uint32" ||
   642  		primitiveType == "uint32[]" ||
   643  		primitiveType == "uint64" ||
   644  		primitiveType == "uint64[]" ||
   645  		primitiveType == "uint96" ||
   646  		primitiveType == "uint96[]" ||
   647  		primitiveType == "uint128" ||
   648  		primitiveType == "uint128[]" ||
   649  		primitiveType == "uint256" ||
   650  		primitiveType == "uint256[]" {
   651  		return true
   652  	}
   653  	return false
   654  }
   655  
   656  // validate checks if the given domain is valid, i.e. contains at least
   657  // the minimum viable keys and values
   658  func (domain *TypedDataDomain) validate() error {
   659  	if domain.ChainId == nil && len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 {
   660  		return errors.New("domain is undefined")
   661  	}
   662  
   663  	return nil
   664  }
   665  
   666  // Map is a helper function to generate a map version of the domain
   667  func (domain *TypedDataDomain) Map() map[string]interface{} {
   668  	dataMap := map[string]interface{}{}
   669  
   670  	if domain.ChainId != nil {
   671  		dataMap["chainId"] = domain.ChainId
   672  	}
   673  
   674  	if len(domain.Name) > 0 {
   675  		dataMap["name"] = domain.Name
   676  	}
   677  
   678  	if len(domain.Version) > 0 {
   679  		dataMap["version"] = domain.Version
   680  	}
   681  
   682  	if len(domain.VerifyingContract) > 0 {
   683  		dataMap["verifyingContract"] = domain.VerifyingContract
   684  	}
   685  
   686  	if len(domain.Salt) > 0 {
   687  		dataMap["salt"] = domain.Salt
   688  	}
   689  	return dataMap
   690  }
   691  
   692  // NameValueType is a very simple struct with Name, Value and Type. It's meant for simple
   693  // json structures used to communicate signing-info about typed data with the UI
   694  type NameValueType struct {
   695  	Name  string      `json:"name"`
   696  	Value interface{} `json:"value"`
   697  	Typ   string      `json:"type"`
   698  }
   699  
   700  // Pprint returns a pretty-printed version of nvt
   701  func (nvt *NameValueType) Pprint(depth int) string {
   702  	output := bytes.Buffer{}
   703  	output.WriteString(strings.Repeat("\u00a0", depth*2))
   704  	output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ))
   705  	if nvts, ok := nvt.Value.([]*NameValueType); ok {
   706  		output.WriteString("\n")
   707  		for _, next := range nvts {
   708  			sublevel := next.Pprint(depth + 1)
   709  			output.WriteString(sublevel)
   710  		}
   711  	} else {
   712  		if nvt.Value != nil {
   713  			output.WriteString(fmt.Sprintf("%q\n", nvt.Value))
   714  		} else {
   715  			output.WriteString("\n")
   716  		}
   717  	}
   718  	return output.String()
   719  }