diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..76e8e5e1 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,32 @@ +name: release + +on: + push: + tags: + - '*' +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: '>=1.19.2' + cache: true + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v3 + with: + distribution: goreleaser + version: latest + args: release --rm-dist + workdir: go-starknet + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TOKEN: ${{ secrets.HOMEBREW_TOKEN }} diff --git a/.github/workflows/go.yml b/.github/workflows/test.yml similarity index 96% rename from .github/workflows/go.yml rename to .github/workflows/test.yml index 5dc4e3bc..1472c359 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,11 @@ -name: Go +name: test on: push: + branches: + - '**' + tags-ignore: + - '**' schedule: - cron: "42 2 * * *" diff --git a/README.md b/README.md index 2d8dbd90..6488eecc 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ - - Go + + test

diff --git a/go-starknet/.gitignore b/go-starknet/.gitignore new file mode 100644 index 00000000..849ddff3 --- /dev/null +++ b/go-starknet/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/go-starknet/.goreleaser.yaml b/go-starknet/.goreleaser.yaml new file mode 100644 index 00000000..7cb1f2fe --- /dev/null +++ b/go-starknet/.goreleaser.yaml @@ -0,0 +1,47 @@ +project_name: go-starknet +# This is an example .goreleaser.yml file with some sensible defaults. +# Make sure to check the documentation at https://goreleaser.com +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy + # you may remove this if you don't need go generate + - go generate ./... +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - darwin +archives: + - replacements: + darwin: darwin + linux: linux + windows: windows + 386: i386 + amd64: x86_64 +brews: + - name: go-starknet + homepage: "https://github.com/dontpanicdao/caigo" + tap: + owner: dontpanicdao + name: homebrew-dontpanicdao + token: "{{ .Env.HOMEBREW_TOKEN }}" + commit_author: + name: gregoryguillou + email: 10611760+gregoryguillou@users.noreply.github.com + +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' + +# modelines, feel free to remove those if you don't want/use them: +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# vim: set ts=2 sw=2 tw=0 fo=cnqoj diff --git a/go-starknet/README.md b/go-starknet/README.md new file mode 100644 index 00000000..abaad676 --- /dev/null +++ b/go-starknet/README.md @@ -0,0 +1,22 @@ +# go-starknet + +`go-starknet` is a CLI written in Go for Starknet. To install it, you can +simply: + +## if you have Go 1.18+ installed + +```shell +cd +go install github.com/dontpanicdao/caigo/go-starknet@latest +go-starknet help +``` + +## on MacOS with Homebrew + +```shell +cd +brew tap dontpanicdao/dontpanicdao +brew install go-starknet +go-starknet help +``` + diff --git a/go-starknet/block.go b/go-starknet/block.go index 588ba384..bc91709a 100644 --- a/go-starknet/block.go +++ b/go-starknet/block.go @@ -13,6 +13,14 @@ import ( "github.com/urfave/cli/v2" ) +var blockCommand = cli.Command{ + Name: "get_block", + Aliases: []string{"b"}, + Usage: "get a block", + Flags: blockFlags, + Action: block, +} + type friendlyBlock gateway.Block func (f friendlyBlock) MarshalJSON() ([]byte, error) { diff --git a/go-starknet/main.go b/go-starknet/main.go index 560fcfc8..11b0a8cf 100644 --- a/go-starknet/main.go +++ b/go-starknet/main.go @@ -2,15 +2,9 @@ package main import ( _ "embed" - "encoding/json" - "errors" - "fmt" "log" - "math/big" "os" - "github.com/dontpanicdao/caigo" - "github.com/dontpanicdao/caigo/types" "github.com/urfave/cli/v2" ) @@ -20,102 +14,10 @@ var dictionary []byte func main() { app := &cli.App{ Commands: []*cli.Command{ - { - Name: "get_block", - Aliases: []string{"b"}, - Usage: "get a block", - Flags: blockFlags, - Action: block, - }, - { - Name: "get_transaction", - Aliases: []string{"t"}, - Usage: "get a transaction", - Flags: transactionFlags, - Action: transaction, - }, - { - Name: "utils", - Aliases: []string{"u"}, - Usage: "utilities to encode/decode felt and entrypoints", - Subcommands: []*cli.Command{ - { - Name: "felt", - Usage: "display felts in different formats", - Action: func(cCtx *cli.Context) error { - for _, v := range cCtx.Args().Slice() { - fmt.Printf("value: %s\n", v) - vInt, ok := big.NewInt(0).SetString(v, 0) - if !ok { - fmt.Println("could not guess format") - } - fmt.Printf("decimal: %s\n", vInt.Text(10)) - fmt.Printf("hex: 0x%s\n", vInt.Text(16)) - fmt.Println() - } - return nil - }, - }, - { - Name: "entrypoint", - Usage: "display entrypoints in different formats", - Action: func(cCtx *cli.Context) error { - for _, v := range cCtx.Args().Slice() { - fmt.Printf("value: %s\n", v) - fmt.Printf("decimal: %s\n", types.GetSelectorFromName(v).Text(10)) - fmt.Printf("hex: 0x%s\n", types.GetSelectorFromName(v).Text(16)) - fmt.Println() - } - return nil - }, - }, - { - Name: "guess", - Usage: "guess an entrypoint from a dictionary", - Action: func(cCtx *cli.Context) error { - dict := map[string]string{} - err := json.Unmarshal(dictionary, &dict) - if err != nil { - return err - } - v := cCtx.Args().First() - vInt, ok := big.NewInt(0).SetString(v, 0) - if !ok { - return errors.New("not a number") - } - value, ok := dict[fmt.Sprintf("0x%s", vInt.Text(16))] - if !ok { - fmt.Println("key not in dictionary") - return nil - } - fmt.Printf("guess: %s\n", value) - fmt.Printf("decimal: %s\n", types.GetSelectorFromName(value).Text(10)) - fmt.Printf("hex: 0x%s\n", types.GetSelectorFromName(value).Text(16)) - fmt.Println() - return nil - }, - }, - { - Name: "publickey", - Usage: "get a public key from the private key", - Action: func(cCtx *cli.Context) error { - - privateKey := cCtx.Args().First() - privateKeyInt, ok := big.NewInt(0).SetString(privateKey, 0) - if !ok { - return errors.New("not a number") - } - publicKey, _, err := caigo.Curve.PrivateToPoint(privateKeyInt) - if err != nil { - return err - } - fmt.Printf("public key: 0x%s\n", publicKey.Text(16)) - fmt.Println() - return nil - }, - }, - }, - }, + &blockCommand, + &transactionCommand, + &utilsCommand, + &profileCommand, }, } diff --git a/go-starknet/profile.go b/go-starknet/profile.go new file mode 100644 index 00000000..3e7d8aea --- /dev/null +++ b/go-starknet/profile.go @@ -0,0 +1,130 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/urfave/cli/v2" +) + +const profileDirectory = ".go-starknet" +const profileFilename = "profile.json" + +type Profile struct { + DefaultFormat string `json:"defaultFormat,omitempty"` +} + +var profileCommand = cli.Command{ + Name: "profile", + Aliases: []string{"p"}, + Usage: "manage the user profile", + Subcommands: []*cli.Command{ + { + Name: "list", + Usage: "go-starknet profile list", + Action: profileList, + }, + { + Name: "set", + Usage: "go-starknet profile set name=value", + Action: profileSet, + }, + }, +} + +func initOrLoadProfile() (*Profile, error) { + home, err := os.UserHomeDir() + if err != nil { + return nil, err + } + profileFullDirectory := filepath.Join(home, profileDirectory) + v, err := os.Stat(profileFullDirectory) + if err != nil && errors.Is(err, os.ErrNotExist) { + err = os.MkdirAll(profileFullDirectory, 0755) + if err != nil { + return nil, err + } + v, err = os.Stat(profileFullDirectory) + } + if err != nil { + return nil, err + } + if !v.IsDir() { + return nil, fmt.Errorf("%s not directory", v.Name()) + } + profileFullFilename := filepath.Join(profileFullDirectory, profileFilename) + content, err := os.ReadFile(profileFullFilename) + p := Profile{} + if err != nil && errors.Is(err, os.ErrNotExist) { + content, err = json.MarshalIndent(p, " ", " ") + if err != nil { + return nil, err + } + err = os.WriteFile(profileFullFilename, content, 0755) + if err != nil { + return nil, err + } + return &p, nil + } + if err != nil { + return nil, err + } + err = json.Unmarshal(content, &p) + return &p, err +} + +func (p Profile) save() error { + home, err := os.UserHomeDir() + if err != nil { + return err + } + profileFullFilename := filepath.Join(home, profileDirectory, profileFilename) + content, err := json.MarshalIndent(p, " ", " ") + if err != nil { + return err + } + return os.WriteFile(profileFullFilename, content, 0755) +} + +func or(a string, b string) string { + if a != "" { + return a + } + return b +} + +func profileList(cCtx *cli.Context) error { + p, err := initOrLoadProfile() + if err != nil { + return err + } + fmt.Printf("profile\n") + fmt.Printf(" format: %s\n", or(p.DefaultFormat, "friendly")) + return nil +} + +func profileSet(cCtx *cli.Context) error { + p, err := initOrLoadProfile() + if err != nil { + return err + } + values := cCtx.Args() + if len(values.Slice()) != 1 { + fmt.Printf("define a value %+v\n", values) + os.Exit(1) + } + changed := false + k, v, ok := strings.Cut(values.First(), "=") + if ok && strings.ToLower(k) == "format" { + p.DefaultFormat = v + changed = true + } + if changed { + p.save() + } + return nil +} diff --git a/go-starknet/transaction.go b/go-starknet/transaction.go index 5af073dc..86f4e4e7 100644 --- a/go-starknet/transaction.go +++ b/go-starknet/transaction.go @@ -10,6 +10,14 @@ import ( "github.com/urfave/cli/v2" ) +var transactionCommand = cli.Command{ + Name: "get_transaction", + Aliases: []string{"t"}, + Usage: "get a transaction", + Flags: transactionFlags, + Action: transaction, +} + type friendlyTransaction gateway.StarknetTransaction func (f friendlyTransaction) MarshalJSON() ([]byte, error) { diff --git a/go-starknet/utils.go b/go-starknet/utils.go new file mode 100644 index 00000000..ad02fb80 --- /dev/null +++ b/go-starknet/utils.go @@ -0,0 +1,95 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/dontpanicdao/caigo" + "github.com/dontpanicdao/caigo/types" + "github.com/urfave/cli/v2" +) + +var utilsCommand = cli.Command{ + Name: "utils", + Aliases: []string{"u"}, + Usage: "utilities to encode/decode felt and entrypoints", + Subcommands: []*cli.Command{ + { + Name: "felt", + Usage: "display felts in different formats", + Action: func(cCtx *cli.Context) error { + for _, v := range cCtx.Args().Slice() { + fmt.Printf("value: %s\n", v) + vInt, ok := big.NewInt(0).SetString(v, 0) + if !ok { + fmt.Println("could not guess format") + } + fmt.Printf("decimal: %s\n", vInt.Text(10)) + fmt.Printf("hex: 0x%s\n", vInt.Text(16)) + fmt.Println() + } + return nil + }, + }, + { + Name: "entrypoint", + Usage: "display entrypoints in different formats", + Action: func(cCtx *cli.Context) error { + for _, v := range cCtx.Args().Slice() { + fmt.Printf("value: %s\n", v) + fmt.Printf("decimal: %s\n", types.GetSelectorFromName(v).Text(10)) + fmt.Printf("hex: 0x%s\n", types.GetSelectorFromName(v).Text(16)) + fmt.Println() + } + return nil + }, + }, + { + Name: "guess", + Usage: "guess an entrypoint from a dictionary", + Action: func(cCtx *cli.Context) error { + dict := map[string]string{} + err := json.Unmarshal(dictionary, &dict) + if err != nil { + return err + } + v := cCtx.Args().First() + vInt, ok := big.NewInt(0).SetString(v, 0) + if !ok { + return errors.New("not a number") + } + value, ok := dict[fmt.Sprintf("0x%s", vInt.Text(16))] + if !ok { + fmt.Println("key not in dictionary") + return nil + } + fmt.Printf("guess: %s\n", value) + fmt.Printf("decimal: %s\n", types.GetSelectorFromName(value).Text(10)) + fmt.Printf("hex: 0x%s\n", types.GetSelectorFromName(value).Text(16)) + fmt.Println() + return nil + }, + }, + { + Name: "publickey", + Usage: "get a public key from the private key", + Action: func(cCtx *cli.Context) error { + + privateKey := cCtx.Args().First() + privateKeyInt, ok := big.NewInt(0).SetString(privateKey, 0) + if !ok { + return errors.New("not a number") + } + publicKey, _, err := caigo.Curve.PrivateToPoint(privateKeyInt) + if err != nil { + return err + } + fmt.Printf("public key: 0x%s\n", publicKey.Text(16)) + fmt.Println() + return nil + }, + }, + }, +}