Skip to content

Commit

Permalink
Merge pull request #5 from moonstream-to/evm-support
Browse files Browse the repository at this point in the history
Code generation for EVM smart contracts
  • Loading branch information
zomglings authored Feb 5, 2024
2 parents 1f51b05 + 5f141e9 commit 32fd3f4
Show file tree
Hide file tree
Showing 13 changed files with 5,646 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ go.work

# Seer ignores
seer
.vscode/
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ seer:
go build .

clean:
rm -f ownable-erc-721
rm -f seer

rebuild: clean build
rebuild: clean build

examples/ownable-erc-721/OwnableERC721.go: rebuild
./seer evm generate --abi fixtures/OwnableERC721.json --bytecode fixtures/OwnableERC721.bin --cli --package main --struct OwnableERC721 --output examples/ownable-erc-721/OwnableERC721.go --includemain

ownable-erc-721: examples/ownable-erc-721/OwnableERC721.go
go build ./examples/ownable-erc-721
95 changes: 92 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# seer

`seer` is Moonstream's second generation EVM chain crawler.
`seer` is Moonstream's second generation blockchain-adjacent tooling for crawling data and performing
smart contract interactions.

It builds on what we have learned from our first generation of [`crawlers`](https://github.com/moonstream-to/api/tree/e69d81d1fb081cbddb0c8a1983af41e53d5a0f8f/crawlers).
It builds on what we have learned from our first generation of [`crawlers`](https://github.com/moonstream-to/api/tree/e69d81d1fb081cbddb0c8a1983af41e53d5a0f8f/crawlers) and from [`moonworm`](https://github.com/moonstream-to/moonworm).

## Build

Expand Down Expand Up @@ -30,7 +31,7 @@ To generate the Go bindings to a Starknet contract from its ABI, run:
seer starknet generate --abi $ABI_FILE --package $GO_PACKAGE_NAME
```

1. `$ABI_FILE` should be the path to the JSON file containing the Starknet contract ABI
1. `$ABI_FILE` should be the path to the JSON file containing the Starknet contract ABI.
2. `$GO_PACKAGE_NAME` should be the name of the Go package that the generated code will belong to. If specified,
the line `package $GO_PACKAGE_NAME` will be emitted at the top of the generated code. If not specified, no
such line is emitted.
Expand All @@ -40,3 +41,91 @@ You can also pipe the ABI JSON into this command rather than specifying the `--a
```bash
jq . $ABI_FILE | seer starknet generate --package $GO_PACKAGE_NAME
```

### Go bindings for Ethereum Virtual Machine (EVM) contracts

To generate the Go bindings to an EVM contract, run:

```bash
seer evm generate \
--abi $ABI_FILE \
--bytecode $BIN_FILE \
--cli \
--package $GO_PACKAGE_NAME \
--struct $GO_STRUCT_NAME
```

1. `$ABI_FILE` should be the path to a JSON file containing the contract's ABI.
2. `$BIN_FILE` should be a path to the file containing the compiled contract bytecode. If the `--bytecode` is not provided,
the bindings are generated with no deployment method.
3. `$GO_PACKAGE_NAME` should be the name of the Go package that the generated code will fall under.
4. `$GO_STRUCT_NAME` should be the name of the struct that you would like to represent an instance of the contract with the given ABI.

If you want to write the output to a file, you can use the `--output` argument to do so. Or shell redirections.

#### Example: `OwnableERC721`

The code in [`examples/ownable-erc-721`](./examples/ownable-erc-721/OwnableERC721.go) was generated from the project root directory using:

```bash
seer evm generate \
--abi fixtures/OwnableERC721.json \
--bytecode fixtures/OwnableERC721.bin \
--cli \
--package main \
--struct OwnableERC721 \
--output examples/ownable-erc-721/OwnableERC721.go
```

To run this code, first build it:

```bash
go build ./examples/ownable-erc-721
```

This will create an executable file called `ownable-erc-721` (on Windows, you may want to rename it to `ownable-erc-721.exe` for convenience).

Try running it:

```
$ ./ownable-erc-721 -h
Interact with the OwnableERC721 contract
Usage:
ownable-erc-721 [flags]
ownable-erc-721 [command]
Commands which deploy contracts
deploy Deploy a new OwnableERC721 contract
Commands which view contract state
balance-of Call the BalanceOf view method on a OwnableERC721 contract
get-approved Call the GetApproved view method on a OwnableERC721 contract
is-approved-for-all Call the IsApprovedForAll view method on a OwnableERC721 contract
name Call the Name view method on a OwnableERC721 contract
owner Call the Owner view method on a OwnableERC721 contract
owner-of Call the OwnerOf view method on a OwnableERC721 contract
supports-interface Call the SupportsInterface view method on a OwnableERC721 contract
symbol Call the Symbol view method on a OwnableERC721 contract
token-uri Call the TokenURI view method on a OwnableERC721 contract
Commands which submit transactions
approve Execute the Approve method on a OwnableERC721 contract
mint Execute the Mint method on a OwnableERC721 contract
renounce-ownership Execute the RenounceOwnership method on a OwnableERC721 contract
safe-transfer-from Execute the SafeTransferFrom method on a OwnableERC721 contract
safe-transfer-from-0 Execute the SafeTransferFrom0 method on a OwnableERC721 contract
set-approval-for-all Execute the SetApprovalForAll method on a OwnableERC721 contract
transfer-from Execute the TransferFrom method on a OwnableERC721 contract
transfer-ownership Execute the TransferOwnership method on a OwnableERC721 contract
Additional Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
Flags:
-h, --help help for ownable-erc-721
Use "ownable-erc-721 [command] --help" for more information about a command.
```
96 changes: 90 additions & 6 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package main

import (
"encoding/json"
"errors"
"go/format"
"io"
"os"
"strings"

"github.com/spf13/cobra"

"github.com/moonstream-to/seer/evm"
"github.com/moonstream-to/seer/starknet"
"github.com/moonstream-to/seer/version"
)
Expand All @@ -26,7 +28,8 @@ func CreateRootCommand() *cobra.Command {
completionCmd := CreateCompletionCommand(rootCmd)
versionCmd := CreateVersionCommand()
starknetCmd := CreateStarknetCommand()
rootCmd.AddCommand(completionCmd, versionCmd, starknetCmd)
evmCmd := CreateEVMCommand()
rootCmd.AddCommand(completionCmd, versionCmd, starknetCmd, evmCmd)

// By default, cobra Command objects write to stderr. We have to forcibly set them to output to
// stdout.
Expand Down Expand Up @@ -104,7 +107,7 @@ func CreateVersionCommand() *cobra.Command {
func CreateStarknetCommand() *cobra.Command {
starknetCmd := &cobra.Command{
Use: "starknet",
Short: "Generate interfaces and crawlers for Starknet",
Short: "Generate interfaces and crawlers for Starknet contracts",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
Expand Down Expand Up @@ -160,7 +163,7 @@ func CreateStarknetGenerateCommand() *cobra.Command {
var rawABI []byte
var readErr error

starknetGenTypesCommand := &cobra.Command{
starknetGenerateCommand := &cobra.Command{
Use: "generate",
Short: "Generate Go bindings for a Starknet contract from its ABI",
PreRunE: func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -201,8 +204,89 @@ func CreateStarknetGenerateCommand() *cobra.Command {
},
}

starknetGenTypesCommand.Flags().StringVarP(&packageName, "package", "p", "", "The name of the package to generate")
starknetGenTypesCommand.Flags().StringVarP(&infile, "abi", "a", "", "Path to contract ABI (default stdin)")
starknetGenerateCommand.Flags().StringVarP(&packageName, "package", "p", "", "The name of the package to generate")
starknetGenerateCommand.Flags().StringVarP(&infile, "abi", "a", "", "Path to contract ABI (default stdin)")

return starknetGenTypesCommand
return starknetGenerateCommand
}

func CreateEVMCommand() *cobra.Command {
evmCmd := &cobra.Command{
Use: "evm",
Short: "Generate interfaces and crawlers for Ethereum Virtual Machine (EVM) contracts",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}

evmGenerateCmd := CreateEVMGenerateCommand()
evmCmd.AddCommand(evmGenerateCmd)

return evmCmd
}

func CreateEVMGenerateCommand() *cobra.Command {
var cli, noformat, includemain bool
var infile, packageName, structName, bytecodefile, outfile string
var rawABI, bytecode []byte
var readErr error

evmGenerateCmd := &cobra.Command{
Use: "generate",
Short: "Generate Go bindings for an EVM contract from its ABI",
PreRunE: func(cmd *cobra.Command, args []string) error {
if packageName == "" {
return errors.New("package name is required via --package/-p")
}
if structName == "" {
return errors.New("struct name is required via --struct/-s")
}

if infile != "" {
rawABI, readErr = os.ReadFile(infile)
} else {
rawABI, readErr = io.ReadAll(os.Stdin)
}

if bytecodefile != "" {
bytecode, readErr = os.ReadFile(bytecodefile)
}

return readErr
},
RunE: func(cmd *cobra.Command, args []string) error {
code, codeErr := evm.GenerateTypes(structName, rawABI, bytecode, packageName)
if codeErr != nil {
return codeErr
}

if cli {
code, readErr = evm.AddCLI(code, structName, noformat, includemain)
if readErr != nil {
return readErr
}
}

if outfile != "" {
writeErr := os.WriteFile(outfile, []byte(code), 0644)
if writeErr != nil {
return writeErr
}
} else {
cmd.Println(code)
}
return nil
},
}

evmGenerateCmd.Flags().StringVarP(&packageName, "package", "p", "", "The name of the package to generate")
evmGenerateCmd.Flags().StringVarP(&structName, "struct", "s", "", "The name of the struct to generate")
evmGenerateCmd.Flags().StringVarP(&infile, "abi", "a", "", "Path to contract ABI (default stdin)")
evmGenerateCmd.Flags().StringVarP(&bytecodefile, "bytecode", "b", "", "Path to contract bytecode (default none - in this case, no deployment method is created)")
evmGenerateCmd.Flags().BoolVarP(&cli, "cli", "c", false, "Add a CLI for interacting with the contract (default false)")
evmGenerateCmd.Flags().BoolVar(&noformat, "noformat", false, "Set this flag if you do not want the generated code to be formatted (useful to debug errors)")
evmGenerateCmd.Flags().BoolVar(&includemain, "includemain", false, "Set this flag if you want to generate a \"main\" function to execute the CLI and make the generated code self-contained - this option is ignored if --cli is not set")
evmGenerateCmd.Flags().StringVarP(&outfile, "output", "o", "", "Path to output file (default stdout)")

return evmGenerateCmd
}
Loading

0 comments on commit 32fd3f4

Please sign in to comment.