From a26ad99eb6a0725b447c380521aaf3a7db3f28e8 Mon Sep 17 00:00:00 2001 From: balaji Date: Wed, 14 Feb 2024 17:13:28 +0530 Subject: [PATCH] poonao template Signed-off-by: balaji --- cmd/ethkit/template.go | 1 + cmd/ethkit/template/rpc.go.tmpl | 31 +++ .../template/rpc_method_expansion.go.tmpl | 17 ++ cmd/ethkit/template/type_expansion.go.tmpl | 21 ++ .../template/type_value_explansion.go.tmpl | 57 ++++ cmd/ethkit/template/webrpc_schema.go.tmpl | 29 ++ cmd/ethkit/webrpc.go | 249 ++++++++++++++++++ cmd/ethkit/webrpc_test.go | 8 + go.mod | 1 + go.sum | 2 + 10 files changed, 416 insertions(+) create mode 100644 cmd/ethkit/template.go create mode 100644 cmd/ethkit/template/rpc.go.tmpl create mode 100644 cmd/ethkit/template/rpc_method_expansion.go.tmpl create mode 100644 cmd/ethkit/template/type_expansion.go.tmpl create mode 100644 cmd/ethkit/template/type_value_explansion.go.tmpl create mode 100644 cmd/ethkit/template/webrpc_schema.go.tmpl create mode 100644 cmd/ethkit/webrpc.go create mode 100644 cmd/ethkit/webrpc_test.go diff --git a/cmd/ethkit/template.go b/cmd/ethkit/template.go new file mode 100644 index 00000000..06ab7d0f --- /dev/null +++ b/cmd/ethkit/template.go @@ -0,0 +1 @@ +package main diff --git a/cmd/ethkit/template/rpc.go.tmpl b/cmd/ethkit/template/rpc.go.tmpl new file mode 100644 index 00000000..7fc7a0af --- /dev/null +++ b/cmd/ethkit/template/rpc.go.tmpl @@ -0,0 +1,31 @@ +{{define "rpc"}} +package {{.Package}} +import( + "context" + "math/big" + "github.com/0xsequence/ethkit/go-ethereum/accounts/abi" + "github.com/0xsequence/ethkit/go-ethereum/common" + "github.com/0xsequence/ethkit/go-ethereum/common/hexutil" + "github.com/0xsequence/go-sequence/lib/prototyp" + proto "{{.RpcPackage}}" +) + +{{ template "WebRpcSchema" .}} + +type Rpc struct { + contractABI abi.ABI +} + +func NewRpc() *Rpc { + abi, err := abi.JSON(strings.NewReader(OrderbookMetaData.ABI)) + if err != nil { + panic(err) + } + return &Rpc{contractABI:abi} +} + + +{{range $methodName,$method := .Methods}} + {{- template "RpcMethodExapansion" dict "MethodName" $methodName "Method" $method}} +{{end}} +{{end}} \ No newline at end of file diff --git a/cmd/ethkit/template/rpc_method_expansion.go.tmpl b/cmd/ethkit/template/rpc_method_expansion.go.tmpl new file mode 100644 index 00000000..0d55966f --- /dev/null +++ b/cmd/ethkit/template/rpc_method_expansion.go.tmpl @@ -0,0 +1,17 @@ +{{define "RpcMethodExapansion"}} +func(r *Rpc) {{firstLetterUpper .MethodName}}(ctx context.Context, {{- range $inputIndex, $input := .Method.Inputs}} {{ $input.Name}} {{argType $input.Type}}{{if len $.Method.Inputs | needArgSeperator $inputIndex}},{{end}}{{end}}) (string, error) { + + {{- range $inputIndex, $input := .Method.Inputs}} + + {{- template "TypeExapansion" dict "Input" $input "ParamIndex" $inputIndex}} + + {{- template "TypeValueExapansion" dict "Input" $input "ParamIndex" $inputIndex}} + + {{- end }} + buf, err := r.contractABI.Methods["{{.MethodName}}"].Inputs.Pack({{- range $inputIndex, $input := .Method.Inputs }}param{{- $inputIndex}} {{- if len $.Method.Inputs | needArgSeperator $inputIndex}},{{end}} {{- end}}) + if err != nil { + return "", err + } + return hexutil.Encode(buf), nil +} +{{- end}} \ No newline at end of file diff --git a/cmd/ethkit/template/type_expansion.go.tmpl b/cmd/ethkit/template/type_expansion.go.tmpl new file mode 100644 index 00000000..d27e3336 --- /dev/null +++ b/cmd/ethkit/template/type_expansion.go.tmpl @@ -0,0 +1,21 @@ +{{define "TypeExapansion"}} +{{if hasStruct .Input.Type}} + param{{.ParamIndex}}:={{if isSlice .Input.Type }}[]{{end}}struct{ + {{- $elem := $.Input.Type -}} + {{- if isSlice .Input.Type }} {{$elem = $.Input.Type.Elem}} {{end }} + {{range $index, $tupleElem := $elem.TupleElems}} + {{- index $elem.TupleRawNames $index | firstLetterUpper}} {{goType $tupleElem.String}} + {{end -}} + }{} +{{else}} + {{- if eq .Input.Type.String "uint256"}} + param{{.ParamIndex}} := big.NewInt(0) + {{- else if eq .Input.Type.String "address"}} + param{{.ParamIndex}} := common.Address{} + {{- else if eq .Input.Type.String "address[]"}} + param{{.ParamIndex}} := []common.Address{} + {{- else if eq .Input.Type.String "uint256[]"}} + param{{.ParamIndex}} := []*big.Int{} + {{end}} +{{- end}} +{{- end}} \ No newline at end of file diff --git a/cmd/ethkit/template/type_value_explansion.go.tmpl b/cmd/ethkit/template/type_value_explansion.go.tmpl new file mode 100644 index 00000000..cd7727d2 --- /dev/null +++ b/cmd/ethkit/template/type_value_explansion.go.tmpl @@ -0,0 +1,57 @@ +{{- define "TypeValueExapansion"}} + +{{- if hasStruct .Input.Type}} + {{if isSlice .Input.Type}} + {{$elem := .Input.Type.Elem}} + for _, tmp := range {{.Input.Name}} { + param{{.ParamIndex}} = append(param{{.ParamIndex}},struct { + {{ range $index, $tupleElem := $elem.TupleElems}} + {{- index $elem.TupleRawNames $index | firstLetterUpper}} {{goType $tupleElem.String }} + {{end -}} + }{ + {{ range $index, $tupleElem := $elem.TupleElems}} + {{- index $elem.TupleRawNames $index | firstLetterUpper}}: tmp.{{index $elem.TupleRawNames $index | firstLetterUpper}}{{structTypeConversion $tupleElem.String}}, + {{end -}} + }) + } + {{else}} + param{{.ParamIndex}} = struct { + {{ range $index, $tupleElem := $.Input.Type.TupleElems}} + {{- index $.Input.Type.TupleRawNames $index | firstLetterUpper}} {{goType $tupleElem.String }} + {{end -}} + }{ + {{ range $index, $tupleElem := $.Input.Type.TupleElems}} + {{- index $.Input.Type.TupleRawNames $index | firstLetterUpper}}: {{$.Input.Name}}.{{index $.Input.Type.TupleRawNames $index | firstLetterUpper}}{{structTypeConversion $tupleElem.String}}, + {{end -}} + } + {{end}} + +{{else if eq .Input.Type.String "uint256"}} + _, ok{{.ParamIndex}} := param{{.ParamIndex}}.SetString({{.Input.Name}}, 10) + if !ok{{.ParamIndex}} { + return "", proto.Errorf(proto.ErrInvalidArgument,"exptected int type string but got %s", {{.Input.Name}}) + } +{{- else if eq .Input.Type.String "address"}} + + param{{.ParamIndex}} = common.HexToAddress({{.Input.Name}}) + +{{- else if eq .Input.Type.String "uint256[]"}} + + for _, tmp := range {{.Input.Name}} { + bigTmp := big.NewInt(0) + _, ok{{.ParamIndex}} := bigTmp.SetString(tmp,10) + if !ok{{.ParamIndex}} { + return "", proto.Errorf(proto.ErrInvalidArgument,"exptected int type string but got %v", {{.Input.Name}}) + } + param{{.ParamIndex}} = append(param{{.ParamIndex}}, bigTmp) + } + +{{- else if eq .Input.Type.String "address[]"}} + + for _, tmp := range {{.Input.Name}} { + addressTmp := common.HexToAddress(tmp) + param{{.ParamIndex}} = append(param{{.ParamIndex}}, addressTmp) + } + +{{- end}} +{{end}} \ No newline at end of file diff --git a/cmd/ethkit/template/webrpc_schema.go.tmpl b/cmd/ethkit/template/webrpc_schema.go.tmpl new file mode 100644 index 00000000..bb2c1a4b --- /dev/null +++ b/cmd/ethkit/template/webrpc_schema.go.tmpl @@ -0,0 +1,29 @@ +{{define "WebRpcSchema"}} +/** +webrpc schema + +{{range $struct := .Structs}} +struct {{$struct.TupleRawName}} +{{- range $index, $elem := $struct.TupleElems}} + {{- if eq $elem.String "bool"}} + - {{index $struct.TupleRawNames $index}}: bool + {{- else if eq $elem.String "address"}} + - {{index $struct.TupleRawNames $index}}: string + + go.field.type = prototyp.Hash + {{- else if eq $elem.String "uint256"}} + - {{index $struct.TupleRawNames $index}}: string + + go.field.type = prototyp.BigInt + {{- else if eq $elem.String "uint96"}} + - {{index $struct.TupleRawNames $index}}: string + + go.field.type = prototyp.BigInt + {{- end}} +{{- end}} +{{end}} + +service {{firstLetterUpper .Package}} +{{range $methodName, $method := .Methods}} +- {{firstLetterUpper $methodName}}({{- range $inputIndex, $input := $method.Inputs}} {{ $input.Name}}: {{webrpcArgType $input.Type}}{{if len $method.Inputs | needArgSeperator $inputIndex}},{{end}}{{end}}) => (output: string) +{{end}} +**/ +{{end}} + diff --git a/cmd/ethkit/webrpc.go b/cmd/ethkit/webrpc.go new file mode 100644 index 00000000..8a2d1e7a --- /dev/null +++ b/cmd/ethkit/webrpc.go @@ -0,0 +1,249 @@ +package main + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/0xsequence/ethkit/ethartifact" + "github.com/0xsequence/ethkit/go-ethereum/accounts/abi" + "github.com/spf13/cobra" +) + +func init() { + werbrpc := &Webrpc{} + cmd := &cobra.Command{ + Use: "webrpc", + Short: "webrpc generator", + Run: werbrpc.Run, + } + rootCmd.AddCommand(cmd) +} + +type Webrpc struct { +} + +func (w *Webrpc) Run(cmd *cobra.Command, args []string) { + artifact, err := ethartifact.ParseArtifactFile("/home/saint/marketplace-api/lib/contracts/artifacts/marketplace/ISequenceMarket.sol/ISequenceMarket.json") + if err != nil { + panic(err) + return + } + abilocal, err := abi.JSON(strings.NewReader(string(artifact.ABI))) + if err != nil { + panic(err) + } + + funcMap := template.FuncMap{ + // The name "title" is what the function will be called in the template text. + "hasStruct": hasStruct, + "goType": goType, + "firstLetterUpper": firstLetterUpper, + "argType": argType, + "needArgSeperator": needArgSeperator, + "needStructTypeConverstion": needStructTypeConverstion, + "structTypeConversion": structTypeConversion, + "dict": dict, + "isSlice": isSlice, + "webrpcArgType": webrpcArgType, + } + methods := map[string]abi.Method{"createRequestBatch": abilocal.Methods["createRequestBatch"]} + + methods = abilocal.Methods + files, err := filepath.Glob("./template/*.go.tmpl") + if err != nil { + panic(err) + } + t, err := template.New("rpc").Funcs(funcMap).ParseFiles(files...) + if err != nil { + panic(err) + } + + err = t.ExecuteTemplate(os.Stdout, "rpc", map[string]interface{}{"Methods": methods, "Structs": getStructTypes(methods), "RpcPackage": "github.com/0xsequence/marketplace-api/proto", "Package": "orderbook"}) + if err != nil { + panic(err) + } +} + +// hasStruct returns an indicator whether the given type is struct, struct slice +// or struct array. +func hasStruct(t abi.Type) bool { + switch t.T { + case abi.SliceTy: + return hasStruct(*t.Elem) + case abi.ArrayTy: + return hasStruct(*t.Elem) + case abi.TupleTy: + return true + default: + return false + } +} + +func isSlice(t abi.Type) bool { + switch t.T { + case abi.SliceTy: + return true + case abi.AddressTy: + return true + default: + return false + } +} + +func goType(ethType string) string { + switch ethType { + case "uint256": + return "*big.Int" + case "uint96": + return "*big.Int" + case "address": + return "common.Address" + case "address[]": + return "[]common.Address" + case "uint256[]": + return "[]*big.Int" + case "bool": + return "bool" + default: + panic(fmt.Sprintf("goType is not defined for eth type %s", ethType)) + } +} + +func needStructTypeConverstion(ethType string) bool { + switch ethType { + case "uint256": + return true + case "uint96": + return true + case "address": + return true + case "bool": + return false + default: + panic(fmt.Sprintf("type conversion not defined for struct %s", ethType)) + } +} + +func structTypeConversion(ethType string) string { + switch ethType { + case "uint256": + return ".Int()" + case "uint96": + return ".Int()" + case "address": + return ".ToAddress()" + default: + return "" + } +} + +func argType(arg abi.Type) string { + if hasStruct(arg) { + switch arg.T { + case abi.SliceTy: + return "[]" + argType(*arg.Elem) + case abi.ArrayTy: + return "[]" + argType(*arg.Elem) + default: + return "*proto." + arg.TupleRawName + } + } + switch arg.String() { + case "address": + return "string" + case "address[]": + return "[]string" + case "uint256": + return "string" + case "uint256[]": + return "[]string" + default: + panic(fmt.Sprintf("arg type not defined for %s", arg.String())) + } + +} + +func webrpcArgType(arg abi.Type) string { + if hasStruct(arg) { + switch arg.T { + case abi.SliceTy: + return "[]" + webrpcArgType(*arg.Elem) + case abi.ArrayTy: + return "[]" + webrpcArgType(*arg.Elem) + default: + return arg.TupleRawName + } + } + switch arg.String() { + case "address": + return "string" + case "address[]": + return "[]string" + case "uint256": + return "string" + case "uint256[]": + return "[]string" + default: + panic(fmt.Sprintf("arg type not defined for %s", arg.String())) + } +} + +func getStructTypes(methods map[string]abi.Method) []abi.Type { + unique := map[string]interface{}{} + structTypes := []abi.Type{} + for _, method := range methods { + for _, input := range method.Inputs { + if !hasStruct(input.Type) { + continue + } + s := getStruct(input.Type) + _, ok := unique[s.TupleRawName] + if ok { + continue + } + unique[s.TupleRawName] = true + structTypes = append(structTypes, s) + } + } + return structTypes +} + +func getStruct(t abi.Type) abi.Type { + switch t.T { + case abi.TupleTy: + return t + case abi.SliceTy: + return getStruct(*t.Elem) + case abi.AddressTy: + return getStruct(*t.Elem) + default: + panic("failed to get struct") + } +} + +func firstLetterUpper(in string) string { + return strings.ToUpper(in[:1]) + in[1:] +} + +func needArgSeperator(argIndex int, noOfArg int) bool { + return argIndex < noOfArg-1 +} + +func dict(values ...interface{}) (map[string]interface{}, error) { + if len(values)%2 != 0 { + return nil, errors.New("invalid dict call") + } + dict := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, errors.New("dict keys must be strings") + } + dict[key] = values[i+1] + } + return dict, nil +} diff --git a/cmd/ethkit/webrpc_test.go b/cmd/ethkit/webrpc_test.go new file mode 100644 index 00000000..a607768b --- /dev/null +++ b/cmd/ethkit/webrpc_test.go @@ -0,0 +1,8 @@ +package main + +import "testing" + +func TestWebrpc(t *testing.T) { + rpc := &Webrpc{} + rpc.Run(nil, []string{}) +} diff --git a/go.mod b/go.mod index ffa28ae6..8ca3bbf1 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/redis/go-redis/v9 v9.0.5 // indirect + github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/term v0.5.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index da567573..baf646af 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= +github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=