Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func GenWrapper(abiPath, binPath, className, pkgName, outDirSuffixInput string)
gethwrappers.Exit("could not get working directory", err)
}
outDir := filepath.Join(cwd, "generated", outDirSuffixInput, pkgName)
if mkdErr := os.MkdirAll(outDir, 0700); err != nil {
if mkdErr := os.MkdirAll(outDir, 0700); mkdErr != nil {
gethwrappers.Exit(
fmt.Sprintf("failed to create wrapper dir, outDirSuffixInput: %s (could be empty)", outDirSuffixInput),
mkdErr)
Expand Down
15 changes: 6 additions & 9 deletions gethwrappers/helpers/abigen.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"go/token"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
Expand All @@ -30,7 +29,7 @@ var GethVersion = fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.
// AbigenArgs is the arguments to the abigen executable. E.g., Bin is the -bin arg.
// Metadata is the only exception, as it is not passed to abigen but rather used to create a separate metadata variable.
type AbigenArgs struct {
Bin, ABI, BuildInfo, Metadata, Out, BuildInfoOut, Type, Pkg string
Bin, ABI, BuildInfo, Metadata, Out, BuildInfoOut, Type, Pkg, AbiGenPath string
}

// compiler defines the compiler section of contract metadata.
Expand Down Expand Up @@ -67,12 +66,10 @@ func Abigen(a AbigenArgs) {
includeMetadata := os.Getenv("metadata") == "true"

var versionResponse bytes.Buffer
abigenExecutablePath := filepath.Join(GetProjectRoot(), "../../../tools/bin/abigen")
abigenVersionCheck := exec.Command(abigenExecutablePath, "--version")
abigenVersionCheck := exec.Command(a.AbiGenPath, "--version")
abigenVersionCheck.Stdout = &versionResponse
if err := abigenVersionCheck.Run(); err != nil {
Exit("no native abigen; you must install it (`make abigen` in the "+
"chainlink root dir)", err)
Exit("no native abigen; you must install it (`make abigen` in the chainlink root dir)", err)
}
version := string(regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`).Find(
versionResponse.Bytes()))
Expand All @@ -89,7 +86,7 @@ func Abigen(a AbigenArgs) {
if a.Bin != "-" {
args = append(args, "-bin", a.Bin)
}
buildCommand := exec.Command(abigenExecutablePath, args...)
buildCommand := exec.Command(a.AbiGenPath, args...)
var buildResponse bytes.Buffer
buildCommand.Stderr = &buildResponse
if err := buildCommand.Run(); err != nil {
Expand All @@ -110,13 +107,13 @@ func genMetadata(abigenArgs AbigenArgs) {
Exit("Error while reading build info file", err)
}
// Unmarshal into BuildInfo struct to filter out unnecessary fields
// and marshal back to JSON bytes afterwards
// and marshal back to JSON bytes afterward
var build buildInfo
err = json.Unmarshal(info, &build)
if err != nil {
Exit("Error while unmarshalling build info JSON", err)
}
// Get version from metadata file, as it contains the commit hash required by etherscan
// Get version from metadata file, as it contains the commit hash required by Etherscan
metadataBytes, err := os.ReadFile(abigenArgs.Metadata)
if err != nil {
Exit("Error while reading metadata file", err)
Expand Down
7 changes: 5 additions & 2 deletions gethwrappers/helpers/extract_bytecode/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ func run() error {
}

// Validate input directory exists
if _, err := os.Stat(*inputDir); os.IsNotExist(err) {
return fmt.Errorf("input directory does not exist: %s", *inputDir)
if _, err := os.Stat(*inputDir); err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("input directory does not exist: %s", *inputDir)
}
return fmt.Errorf("failed to stat input directory %s: %w", *inputDir, err)
}

fmt.Printf("Input dir: %s\n", *inputDir)
Expand Down
100 changes: 10 additions & 90 deletions gethwrappers/helpers/generate/wrap.go
Original file line number Diff line number Diff line change
@@ -1,107 +1,27 @@
package main

import (
"fmt"
"os"
"path/filepath"

gethwrappers "github.com/smartcontractkit/chainlink-evm/gethwrappers/helpers"
"github.com/smartcontractkit/chainlink-evm/gethwrappers/helpers/generate/wrap"
zksyncwrapper "github.com/smartcontractkit/chainlink-evm/gethwrappers/helpers/zksync"
)

var (
rootDir = "../../solc/"
)

func main() {
project := os.Args[1]
className := os.Args[2]
pkgName := os.Args[3]
solcProjectRoot := os.Args[1]
abiGenPath := os.Args[2]
contract := os.Args[3]
pkgName := os.Args[4]

var outDirSuffix string
if len(os.Args) >= 5 {
outDirSuffix = os.Args[4]
} else {
outDirSuffix = "latest"
}
outDirSuffix := "latest"
Copy link
Collaborator Author

@RensR RensR Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrappers are always generated into latest. Teams that wish to adopt this new wrapper generation should also adopt the versioned gethwrappers.


if os.Getenv("ZKSYNC") == "true" {
outDir := getOutDir(outDirSuffix, pkgName)
zksyncBytecodePath := filepath.Join("..", "zkout", className+".sol", className+".json")
zksyncBytecodePath := filepath.Join("..", "zkout", contract+".sol", contract+".json")
zksyncBytecode := zksyncwrapper.ReadBytecodeFromForgeJSON(zksyncBytecodePath)
outPath := filepath.Join(outDir, pkgName+"_zksync.go")
zksyncwrapper.WrapZksyncDeploy(zksyncBytecode, className, pkgName, outPath)
outPath := filepath.Join(wrap.GetOutDir(outDirSuffix, pkgName), pkgName+"_zksync.go")
zksyncwrapper.WrapZksyncDeploy(zksyncBytecode, contract, pkgName, outPath)
} else {
abiPath := rootDir + project + "/" + className + "/" + className + ".sol/" + className + ".abi.json"
metadataPath := rootDir + project + "/" + className + "/" + className + ".sol/" + className + ".metadata.json"
binPath := rootDir + project + "/" + className + "/" + className + ".sol/" + className + ".bin"
buildInfoPath := rootDir + project + "/" + className + "/build/build.json"

GenWrapper(abiPath, binPath, buildInfoPath, metadataPath, className, pkgName, outDirSuffix)
}
}

// GenWrapper generates a contract wrapper for the given contract.
//
// abiPath is the path to the contract's ABI JSON file.
//
// binPath is the path to the contract's binary file, typically with .bin extension.
//
// className is the name of the generated contract class.
//
// pkgName is the name of the package the contract will be generated in. Try
// to follow idiomatic Go package naming conventions where possible.
//
// outDirSuffixInput is the directory suffix to generate the wrapper in. If not provided, the
// wrapper will be generated in the default location. The default location is
// <project>/generated/<pkgName>/<pkgName>.go. The suffix will take place after
// the <project>/generated, so the overridden location would be
// <project>/generated/<outDirSuffixInput>/<pkgName>/<pkgName>.go.
func GenWrapper(abiPath, binPath, buildInfoPath, metadataPath, className, pkgName, outDirSuffixInput string) {
fmt.Println("Generating", pkgName, "contract wrapper")

outDir := getOutDir(outDirSuffixInput, pkgName)
outPath := filepath.Join(outDir, pkgName+".go")
metadataOutPath := filepath.Join(outDir, pkgName+"_metadata.go")

gethwrappers.Abigen(gethwrappers.AbigenArgs{
Bin: binPath,
ABI: abiPath,
BuildInfo: buildInfoPath,
Metadata: metadataPath,
Out: outPath,
BuildInfoOut: metadataOutPath,
Type: className,
Pkg: pkgName,
})

// Build succeeded, so update the versions db with the new contract data
versions, err := gethwrappers.ReadVersionsDB()
if err != nil {
gethwrappers.Exit("could not read current versions database", err)
}
versions.GethVersion = gethwrappers.GethVersion
versions.ContractVersions[pkgName] = gethwrappers.ContractVersion{
Hash: gethwrappers.VersionHash(abiPath, binPath),
AbiPath: abiPath,
BinaryPath: binPath,
wrap.GenWrapper(solcProjectRoot, contract, pkgName, outDirSuffix, abiGenPath)
}
if err := gethwrappers.WriteVersionsDB(versions); err != nil {
gethwrappers.Exit("could not save versions db", err)
}
}

func getOutDir(outDirSuffixInput, pkgName string) string {
cwd, err := os.Getwd() // gethwrappers directory
if err != nil {
gethwrappers.Exit("could not get working directory", err)
}
outDir := filepath.Join(cwd, "generated", outDirSuffixInput, pkgName)
if mkdErr := os.MkdirAll(outDir, 0700); err != nil {
gethwrappers.Exit(
fmt.Sprintf("failed to create wrapper dir, outDirSuffixInput: %s (could be empty)", outDirSuffixInput),
mkdErr)
}

return outDir
}
78 changes: 78 additions & 0 deletions gethwrappers/helpers/generate/wrap/wrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package wrap

import (
"fmt"
"os"
"path/filepath"

gethwrappers "github.com/smartcontractkit/chainlink-evm/gethwrappers/helpers"
)

// GenWrapper generates a contract wrapper for the given contract.
//
// solcProjectRoot is the path to the solc artifacts for the project, e.g. `"../../contracts/solc/" + project`
// abiPath is the path to the contract's ABI JSON file.
// binPath is the path to the contract's binary file, typically with .bin extension.
// contract is the name of the generated contract class.
// pkgName is the name of the package the contract will be generated in. Try
// to follow idiomatic Go package naming conventions where possible.
//
// outDirSuffixInput is the directory suffix to generate the wrapper in. If not provided, the
// wrapper will be generated in the default location. The default location is
// <project>/generated/<pkgName>/<pkgName>.go. The suffix will take place after
// the <project>/generated, so the overridden location would be
// <project>/generated/<outDirSuffixInput>/<pkgName>/<pkgName>.go.
func GenWrapper(solcProjectRoot, contract, pkgName, outDirSuffixInput, abiGenPath string) {
abiPath := filepath.Join(solcProjectRoot, contract, contract+".sol", contract+".abi.json")
metadataPath := filepath.Join(solcProjectRoot, contract, contract+".sol", contract+".metadata.json")
binPath := filepath.Join(solcProjectRoot, contract, contract+".sol", contract+".bin")
buildInfoPath := filepath.Join(solcProjectRoot, contract, "build", "build.json")

fmt.Println("Generating", pkgName, "contract wrapper")

outDir := GetOutDir(outDirSuffixInput, pkgName)
outPath := filepath.Join(outDir, pkgName+".go")
metadataOutPath := filepath.Join(outDir, pkgName+"_metadata.go")

gethwrappers.Abigen(gethwrappers.AbigenArgs{
Bin: binPath,
ABI: abiPath,
BuildInfo: buildInfoPath,
Metadata: metadataPath,
Out: outPath,
BuildInfoOut: metadataOutPath,
Type: contract,
Pkg: pkgName,
AbiGenPath: abiGenPath,
})

// Build succeeded, so update the versions db with the new contract data
versions, err := gethwrappers.ReadVersionsDB()
if err != nil {
gethwrappers.Exit("could not read current versions database", err)
}
versions.GethVersion = gethwrappers.GethVersion
versions.ContractVersions[pkgName] = gethwrappers.ContractVersion{
Hash: gethwrappers.VersionHash(abiPath, binPath),
AbiPath: abiPath,
BinaryPath: binPath,
}
if err := gethwrappers.WriteVersionsDB(versions); err != nil {
gethwrappers.Exit("could not save versions db", err)
}
}

func GetOutDir(outDirSuffixInput, pkgName string) string {
cwd, err := os.Getwd() // gethwrappers directory
if err != nil {
gethwrappers.Exit("could not get working directory", err)
}
outDir := filepath.Join(cwd, "generated", outDirSuffixInput, pkgName)
if mkdErr := os.MkdirAll(outDir, 0700); mkdErr != nil {
gethwrappers.Exit(
fmt.Sprintf("failed to create wrapper dir, outDirSuffixInput: %s (could be empty)", outDirSuffixInput),
mkdErr)
}

return outDir
}
20 changes: 1 addition & 19 deletions gethwrappers/helpers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"crypto/sha256"
"fmt"
"os"
"path/filepath"
)

// VersionHash is the hash used to detect changes in the underlying contract
Expand All @@ -17,7 +16,7 @@ func VersionHash(abiPath string, binPath string) (hash string) {
if binPath != "-" {
bin, err = os.ReadFile(binPath)
if err != nil {
Exit("Could not read abi path to create version hash", err)
Exit("Could not read bin path to create version hash", err)
}
}
hashMsg := string(abi) + string(bin) + "\n"
Expand All @@ -32,20 +31,3 @@ func Exit(msg string, err error) {
}
os.Exit(1)
}

// GetProjectRoot returns the root of the chainlink project
func GetProjectRoot() (rootPath string) {
root, err := os.Getwd()
if err != nil {
Exit("could not get current working directory while seeking project root",
err)
}
for root != "/" { // Walk up path to find dir containing go.mod
if _, err := os.Stat(filepath.Join(root, "go.mod")); !os.IsNotExist(err) {
return root
}
root = filepath.Dir(root)
}
Exit("could not find project root", nil)
panic("can't get here")
}
Loading