github.com/status-im/status-go@v1.1.0/services/typeddata/types.go (about) 1 package typeddata 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "math/big" 8 "strconv" 9 ) 10 11 const ( 12 eip712Domain = "EIP712Domain" 13 ChainIDKey = "chainId" 14 ) 15 16 // Types define fields for each composite type. 17 type Types map[string][]Field 18 19 // Field stores name and solidity type of the field. 20 type Field struct { 21 Name string `json:"name"` 22 Type string `json:"type"` 23 } 24 25 // Validate checks that both name and type are not empty. 26 func (f Field) Validate() error { 27 if len(f.Name) == 0 { 28 return errors.New("`name` is required") 29 } 30 if len(f.Type) == 0 { 31 return errors.New("`type` is required") 32 } 33 return nil 34 } 35 36 // TypedData defines typed data according to eip-712. 37 type TypedData struct { 38 Types Types `json:"types"` 39 PrimaryType string `json:"primaryType"` 40 Domain map[string]json.RawMessage `json:"domain"` 41 Message map[string]json.RawMessage `json:"message"` 42 } 43 44 // Validate that required fields are defined. 45 // This method doesn't check if dependencies of the main type are defined, it will be validated 46 // when type string is computed. 47 func (t TypedData) Validate() error { 48 if _, exist := t.Types[eip712Domain]; !exist { 49 return fmt.Errorf("`%s` must be in `types`", eip712Domain) 50 } 51 if t.PrimaryType == "" { 52 return errors.New("`primaryType` is required") 53 } 54 if _, exist := t.Types[t.PrimaryType]; !exist { 55 return fmt.Errorf("primary type `%s` not defined in types", t.PrimaryType) 56 } 57 if t.Domain == nil { 58 return errors.New("`domain` is required") 59 } 60 if t.Message == nil { 61 return errors.New("`message` is required") 62 } 63 for typ := range t.Types { 64 fields := t.Types[typ] 65 for i := range fields { 66 if err := fields[i].Validate(); err != nil { 67 return fmt.Errorf("field %d from type `%s` is invalid: %v", i, typ, err) 68 } 69 } 70 } 71 return nil 72 } 73 74 // ValidateChainID accept chain as big integer and verifies if typed data belongs to the same chain. 75 func (t TypedData) ValidateChainID(chain *big.Int) error { 76 if _, exist := t.Domain[ChainIDKey]; !exist { 77 return fmt.Errorf("domain misses chain key %s", ChainIDKey) 78 } 79 var chainID int64 80 if err := json.Unmarshal(t.Domain[ChainIDKey], &chainID); err != nil { 81 var chainIDString string 82 if err = json.Unmarshal(t.Domain[ChainIDKey], &chainIDString); err != nil { 83 return err 84 } 85 if chainID, err = strconv.ParseInt(chainIDString, 0, 64); err != nil { 86 return err 87 } 88 } 89 if chainID != chain.Int64() { 90 return fmt.Errorf("chainId %d doesn't match selected chain %s", chainID, chain) 91 } 92 return nil 93 }