Skip to content

Add zksync compilation scripts #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ae6ec17
Add zksync compilation scripts
ilanolkies Apr 7, 2025
e7efb2d
Amend variable order
ilanolkies Apr 7, 2025
d8d7a25
Add docs
ilanolkies Apr 8, 2025
c368596
Simple generation
ilanolkies Apr 8, 2025
7628716
Add wrapper
ilanolkies Apr 8, 2025
ad037d7
Merge branch 'develop' into zksync
ilanolkies Apr 8, 2025
dd51cac
Merge branch 'zksync' into zksync-gethwrappers
ilanolkies Apr 8, 2025
43cc25f
Generate necessary contracts
ilanolkies Apr 8, 2025
adb849c
Merge branch 'develop' into zksync
ilanolkies Apr 8, 2025
f5e8988
Remove simple tempalte
ilanolkies Apr 8, 2025
6d80e2a
CI
ilanolkies Apr 8, 2025
0b6416d
Remove magic number
ilanolkies Apr 8, 2025
23be654
Update gethwrappers/generation/generate/zksync/vars.go
ilanolkies Apr 8, 2025
21d83e2
Make forge-zksync phony + detect version as abigen
ilanolkies Apr 8, 2025
e2270e6
Detect version
ilanolkies Apr 8, 2025
8063a65
Merge branch 'develop' into zksync
ilanolkies Apr 9, 2025
18a1e06
Revert .github/workflows/solidity-wrappers.yml
ilanolkies Apr 9, 2025
e162c12
Merge branch 'develop' into zksync
ilanolkies Apr 9, 2025
6ca999d
Comment zksync-wrappers
ilanolkies Apr 9, 2025
901f39b
Remove FOUNDRY_PROJECT_SUFFIX variable usage
ilanolkies Apr 16, 2025
c1680ef
Fix FOUNDRY_PROFILE
ilanolkies Apr 21, 2025
4c22d6c
Remove variable
ilanolkies Apr 21, 2025
040f1f9
Use bytecode instead of dir
ilanolkies Apr 21, 2025
18aa182
Merge branch 'develop' into zksync
ilanolkies Apr 22, 2025
6596ad9
Refactor forge json decode
ilanolkies Apr 22, 2025
9cb11c6
Update install_forge_zksync
ilanolkies Apr 22, 2025
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
4 changes: 4 additions & 0 deletions .github/workflows/solidity-wrappers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
# run: make wrappers-all
# working-directory: ./contracts
#
# - name: Run zksync compile and generate wrappers
# run: make wrappers-zksync
# working-directory: ./contracts
#
# - name: Assume role capable of dispatching action
# uses: smartcontractkit/.github/actions/setup-github-token@ef78fa97bf3c77de6563db1175422703e9e6674f # [email protected]
# id: get-gh-token
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/solidity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ jobs:
working-directory: ./chainlink/contracts
run: make wrappers-all

- name: Run zksync compile and generate wrappers
working-directory: ./chainlink/contracts
run: make wrappers-zksync

- name: Check if Go solidity wrappers are updated
if: ${{ needs.changes.outputs.changes == 'true' }}
working-directory: chainlink
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ tmp/
.pnp
.pnp.js
tools/bin/abigen
tools/bin/forge_zksync

/chainlink
core/chainlink
Expand Down
1 change: 1 addition & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ artifacts
cache
node_modules
solc
zkout
abi
coverage
coverage.json
Expand Down
1 change: 1 addition & 0 deletions contracts/FOUNDRY_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Check out the official [Foundry best practices section](https://book.getfoundry.
- Use `make snapshot` to generate the correct snapshot for the selected Foundry profile.
- Use `make snapshot-diff` to see the diff between the local snapshot and your latest changes.
- use `make wrappers` to generate the gethwrappers for the selected Foundry profile.
- use `make wrappers-zksync` to generate the gethwrappers for ZK Sync deployments.
- Use `vm.recordLogs();` to record all logs emitted
- Use `vm.getRecordedLogs()` to get the logs emitted.
- This way you can assert that a log was *not* emitted.
Expand Down
8 changes: 8 additions & 0 deletions contracts/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ mockery: $(mockery) ## Install mockery.
foundry: ## Install foundry.
foundryup --install v1.0.0

.PHONY: forge-zksync
forge-zksync:
../tools/bin/install_forge_zksync

.PHONY: foundry-refresh
foundry-refresh: foundry
git submodule deinit -f .
Expand Down Expand Up @@ -82,6 +86,10 @@ wrappers-all: pnpmdep mockery abigen ## Recompiles solidity contracts and their
# go_generate contains a call to compile all contracts before generating wrappers
go generate ../gethwrappers/go_generate.go

.PHONY: wrappers-zksync
wrappers-zksync: pnpmdep forge-zksync
go generate ../gethwrappers/zksync/go_generate.go

help:
@echo ""
@echo " .__ .__ .__ .__ __"
Expand Down
26 changes: 26 additions & 0 deletions contracts/scripts/zksync_compile_all
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

set -e

echo " ┌──────────────────────────────────────────────┐"
echo " │ Compiling ZKSync contracts... │"
echo " └──────────────────────────────────────────────┘"

CONTRACTS_DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../ && pwd -P )"

compileContract() {
local contract
contract=$(basename "$1")
echo "Compiling" "$contract"

export FOUNDRY_PROFILE="$2"

${CONTRACTS_DIR}/../tools/bin/forge_zksync build $CONTRACTS_DIR/src/v0.8/"$1.sol" --zksync \
--root $CONTRACTS_DIR \
$3
}

compileContract shared/token/ERC677/LinkToken shared "--use 0.8.19"
compileContract shared/token/ERC677/BurnMintERC677 shared "--use 0.8.19"
compileContract vendor/multicall/ebd8b64/src/Multicall3 shared "--use 0.8.19"
compileContract keystone/CapabilitiesRegistry keystone
59 changes: 59 additions & 0 deletions gethwrappers/generation/generate/zksync/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package zksyncwrapper

import (
"context"
"crypto/rand"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/zksync-sdk/zksync2-go/accounts"
"github.com/zksync-sdk/zksync2-go/clients"
"github.com/zksync-sdk/zksync2-go/types"
)

// this file is used as a template. see wrap_zk_bytecode.go before editing
func DeployPlaceholderContractNameZk(deployOpts *accounts.TransactOpts, client *clients.Client, wallet *accounts.Wallet, backend bind.ContractBackend, args ...interface{}) (common.Address, *types.Receipt, *PlaceholderContractName, error) {
var calldata []byte
if len(args) > 0 {
abi, err := PlaceholderContractNameMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
calldata, err = abi.Pack("", args...)
if err != nil {
return common.Address{}, nil, nil, err
}
}

salt := make([]byte, 32)
n, err := rand.Read(salt)
if err != nil {
return common.Address{}, nil, nil, err
}
if n != len(salt) {
return common.Address{}, nil, nil, fmt.Errorf("failed to read random bytes: expected %d, got %d", len(salt), n)
}

txHash, err := wallet.Deploy(deployOpts, accounts.Create2Transaction{
Bytecode: ZkBytecode,
Calldata: calldata,
Salt: salt,
})
if err != nil {
return common.Address{}, nil, nil, err
}

receipt, err := client.WaitMined(context.Background(), txHash)
if err != nil {
return common.Address{}, nil, nil, err
}

address := receipt.ContractAddress
contract, err := NewPlaceholderContractName(address, backend)
if err != nil {
return common.Address{}, nil, nil, err
}

return address, receipt, contract, nil
}
26 changes: 26 additions & 0 deletions gethwrappers/generation/generate/zksync/vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package zksyncwrapper

import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)

// these are mocks for the placeholders used in the template
type PlaceholderContractName struct{}

type IPlaceholderContractNameMetaData struct {
GetAbi func() (*abi.ABI, error)
}

var PlaceholderContractNameMetaData = IPlaceholderContractNameMetaData{
GetAbi: func() (*abi.ABI, error) {
return nil, nil
},
}

var ZkBytecode = []byte{}

func NewPlaceholderContractName(address common.Address, backend bind.ContractBackend) (*PlaceholderContractName, error) {
return nil, nil
}
156 changes: 156 additions & 0 deletions gethwrappers/generation/generate/zksync/zksyncwrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package zksyncwrapper

import (
"bytes"
_ "embed"
"encoding/json"
"fmt"
"go/ast"
"go/format"
"go/token"
"os"
"strings"
)

func ReadBytecodeFromForgeJson(srcFile string) string {
jsonData, err := os.ReadFile(srcFile)
if err != nil {
panic(err)
}

var bytecodeData struct {
Bytecode struct {
Object string `json:"object"`
} `json:"bytecode"`
}
if err := json.Unmarshal(jsonData, &bytecodeData); err != nil {
panic(err)
}

return bytecodeData.Bytecode.Object
}

//go:embed template.go
var zksyncDeployTemplate string

func WrapZksyncDeploy(bytecode, className, pkgName, outPath string) {
fmt.Printf("Generating zk bytecode binding for %s\n", pkgName)

fileNode := &ast.File{
Name: ast.NewIdent(pkgName),
Decls: []ast.Decl{
declareImports(),
declareDeployFunction(className),
declareBytecodeVar(bytecode)}}

writeFile(fileNode, outPath)
}

const comment = `// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
`

var importValues = []string{
`"context"`,
`"crypto/rand"`,
`"fmt"`,
`"github.com/ethereum/go-ethereum/accounts/abi/bind"`,
`"github.com/ethereum/go-ethereum/common"`,
`"github.com/zksync-sdk/zksync2-go/accounts"`,
`"github.com/zksync-sdk/zksync2-go/clients"`,
`"github.com/zksync-sdk/zksync2-go/types"`,
}

func declareImports() ast.Decl {

specs := make([]ast.Spec, len(importValues))
for i, value := range importValues {
specs[i] = &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: value}}
}

return &ast.GenDecl{
Tok: token.IMPORT,
Specs: specs}
}

func declareDeployFunction(contractName string) ast.Decl {
template := zksyncDeployTemplate

sep := "\n"
lines := strings.Split(template, sep)
from := 0
to := 0
// get the func body as string
for !strings.Contains(lines[to], "return address, receipt, contract, nil") {
if strings.Contains(lines[to], "DeployPlaceholderContractNameZk") {
from = to
}
to++
}
template = strings.Join(lines[from+1:to+1], sep)
template = template[1:] // remove the first space
template = strings.Replace(template, "PlaceholderContractName", contractName, 2)

return &ast.FuncDecl{
Name: ast.NewIdent("Deploy" + contractName + "Zk"),
Type: &ast.FuncType{
Params: &ast.FieldList{
List: []*ast.Field{{
Names: []*ast.Ident{ast.NewIdent("deployOpts")},
Type: &ast.Ident{Name: "*accounts.TransactOpts"}}, {
Names: []*ast.Ident{ast.NewIdent("client")},
Type: &ast.Ident{Name: "*clients.Client"}}, {
Names: []*ast.Ident{ast.NewIdent("wallet")},
Type: &ast.Ident{Name: "*accounts.Wallet"}}, {
Names: []*ast.Ident{ast.NewIdent("backend")},
Type: &ast.Ident{Name: "bind.ContractBackend"}}, {
Names: []*ast.Ident{ast.NewIdent("args")},
Type: &ast.Ellipsis{Elt: &ast.Ident{Name: "interface{}"}}}}},
Results: &ast.FieldList{
List: []*ast.Field{
{Type: &ast.Ident{Name: "common.Address"}},
{Type: &ast.Ident{Name: "*types.Receipt"}},
{Type: &ast.StarExpr{X: &ast.Ident{Name: contractName}}},
{Type: &ast.Ident{Name: "error"}}}}},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.ExprStmt{
X: &ast.BasicLit{
Kind: token.STRING,
Value: template}}}}}
}

func declareBytecodeVar(bytecode string) ast.Decl {
return &ast.GenDecl{
Tok: token.VAR,
Specs: []ast.Spec{
&ast.ValueSpec{
Names: []*ast.Ident{ast.NewIdent("ZkBytecode")},
Values: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent("common"),
Sel: ast.NewIdent("Hex2Bytes")},
Args: []ast.Expr{
&ast.BasicLit{
Kind: token.STRING,
Value: fmt.Sprintf(`"%s"`, bytecode)}}}}}}}
}

func writeFile(fileNode *ast.File, dstFile string) {
var buf bytes.Buffer
fset := token.NewFileSet()
if err := format.Node(&buf, fset, fileNode); err != nil {
panic(err)
}

bs := buf.Bytes()
bs = append([]byte(comment), bs...)

if err := os.WriteFile(dstFile, bs, 0600); err != nil {
panic(err)
}
}
30 changes: 30 additions & 0 deletions gethwrappers/generation/zksync/wrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

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

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

func main() {
project := os.Args[1]
contractName := os.Args[2]
packageName := os.Args[3]

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

cwd, err := os.Getwd() // gethwrappers/zksync directory
if err != nil {
gethwrappers.Exit("could not get working directory", err)
}

srcFile := filepath.Join(cwd, "..", "..", "contracts", "zkout", contractName+".sol", contractName+".json")
bytecode := zksyncwrapper.ReadBytecodeFromForgeJson(srcFile)

outPath := filepath.Join(cwd, "..", project, "generated", packageName, packageName+"_zksync.go")

zksyncwrapper.WrapZksyncDeploy(bytecode, contractName, packageName, outPath)
}
Loading
Loading