Live video on the AT Protocol
at eli/sync-tangled 132 lines 2.9 kB view raw
1package schema 2 3import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7 8 "github.com/ethereum/go-ethereum/signer/core/apitypes" 9) 10 11type Schema interface { 12 EIP712() (*EIP712SchemaStruct, error) 13} 14 15type SchemaStruct struct { 16 name string 17 version string 18 schema any 19} 20 21func MakeSchema(name, version string, schema any) (Schema, error) { 22 return &SchemaStruct{ 23 name: name, 24 version: version, 25 schema: schema, 26 }, nil 27} 28 29type EIP712SchemaStruct struct { 30 Types apitypes.Types `json:"types"` 31 Domain *apitypes.TypedDataDomain `json:"domain"` 32 TypeToName map[reflect.Type]string 33 NameToType map[string]reflect.Type 34} 35 36func (schema *SchemaStruct) EIP712() (*EIP712SchemaStruct, error) { 37 var eip712Types = apitypes.Types{ 38 "EIP712Domain": { 39 { 40 Name: "name", 41 Type: "string", 42 }, 43 { 44 Name: "version", 45 Type: "string", 46 }, 47 }, 48 } 49 50 stype := reflect.TypeOf(schema.schema) 51 if stype.Kind() != reflect.Struct { 52 return nil, fmt.Errorf("schema parameter of MakeEIP712Signer is not a struct") 53 } 54 fields := reflect.VisibleFields(stype) 55 typeToName := map[reflect.Type]string{} 56 nameToType := map[string]reflect.Type{} 57 for _, field := range fields { 58 name := field.Name 59 eip712TypeName := fmt.Sprintf("%sData", name) 60 if field.Type.Kind() != reflect.Struct { 61 return nil, fmt.Errorf("field '%s' in provided schema is not a struct", name) 62 } 63 typeToName[field.Type] = name 64 nameToType[name] = field.Type 65 parentType := []apitypes.Type{ 66 { 67 Name: "signer", 68 Type: "address", 69 }, 70 { 71 Name: "time", 72 Type: "int64", 73 }, 74 { 75 Name: "data", 76 Type: eip712TypeName, 77 }, 78 } 79 typeSlice := []apitypes.Type{} 80 81 subfields := reflect.VisibleFields(field.Type) 82 for _, subfield := range subfields { 83 eipType, err := goToEIP712(subfield) 84 if err != nil { 85 return nil, fmt.Errorf("error handling type %s: %w", name, err) 86 } 87 typeSlice = append(typeSlice, eipType) 88 } 89 eip712Types[name] = parentType 90 eip712Types[eip712TypeName] = typeSlice 91 } 92 return &EIP712SchemaStruct{ 93 Types: eip712Types, 94 Domain: &apitypes.TypedDataDomain{ 95 Version: schema.version, 96 Name: schema.name, 97 }, 98 TypeToName: typeToName, 99 NameToType: nameToType, 100 }, nil 101} 102 103func (eip *EIP712SchemaStruct) JSON() ([]byte, error) { 104 out := map[string]any{ 105 "domain": eip.Domain, 106 "types": eip.Types, 107 } 108 bs, err := json.MarshalIndent(out, "", " ") 109 if err != nil { 110 return []byte{}, err 111 } 112 return bs, nil 113} 114 115// turns a go type into an eip712 type 116func goToEIP712(field reflect.StructField) (apitypes.Type, error) { 117 var typ string 118 kind := field.Type.Kind() 119 if kind == reflect.String { 120 typ = "string" 121 } else if kind == reflect.Int64 { 122 typ = "int64" 123 } 124 jsonTag := field.Tag.Get("json") 125 if jsonTag == "" { 126 return apitypes.Type{}, fmt.Errorf("could not find field name for %s", field.Name) 127 } 128 return apitypes.Type{ 129 Name: jsonTag, 130 Type: typ, 131 }, nil 132}