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