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
5 changes: 5 additions & 0 deletions .changeset/clear-beans-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"operations-gen": minor
---

feat: Add zkSync VM bytecode support to operations-gen. Generated Deploy operations can now include `ZkSyncVM` bytecode via `zksync_bytecode` and `zksync_bindings_package` config fields.
4 changes: 3 additions & 1 deletion tools/operations-gen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ contracts:
| `version` | Yes | Config schema version |
| `chain_family` | No | Target chain family. Only `"evm"` is supported. Defaults to `"evm"`. |
| `input.gobindings_package` | No | Parent Go import path or relative filesystem path containing versioned abigen packages. Used to derive contract bindings as `<input.gobindings_package>/<version_path>/<package_name>`. |
| `input.zksync_bindings_package` | No | Default Go import path or relative filesystem path for zkSync VM deploy bytecode. Used when a contract sets `zksync_bytecode` to a symbol only. |
| `output.base_path` | Yes | Root directory where generated files are written. Relative to the config file. |

### Contract fields
Expand All @@ -147,7 +148,8 @@ contracts:
| `gobindings_package` | No | Optional full Go import path or relative filesystem path override for this contract's abigen-generated bindings package. Required only when `input.gobindings_package` is not set. |
| `package_name` | No | Override the generated Go package name. Defaults to `snake_case(contract_name)`. |
| `version_path` | No | Override the directory path derived from the version. Defaults to `v{major}_{minor}_{patch}`. |
| `omit_deploy` | No | Skip generation of the `Deploy` operation and bytecode constant. Defaults to `false`. |
| `omit_deploy` | No | Skip generation of the `Deploy` operation and bytecode constant. Defaults to `false`. Cannot be combined with `zksync_bytecode`. |
| `zksync_bytecode` | No | zkSync VM deploy bytecode symbol, or `{package, symbol}`. Package defaults to `input.zksync_bindings_package`, then the contract's `gobindings_package`. |

### Function access control

Expand Down
6 changes: 6 additions & 0 deletions tools/operations-gen/generate/templates/evm/operations.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import (
cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations"
{{- end}}
gobindings "{{.GobindingsImport}}"
{{- if and .ZkSyncBytecodeSymbol (not .ZkSyncBytecodeUseGobindings)}}
zkbindings "{{.ZkSyncBytecodeImport}}"
{{- end}}
)

var ContractType cldf_deployment.ContractType = "{{.ContractType}}"
Expand Down Expand Up @@ -61,6 +64,9 @@ var Deploy = contract.NewDeploy(contract.DeployParams[ConstructorArgs]{
BytecodeByTypeAndVersion: map[string]contract.Bytecode{
cldf_deployment.NewTypeAndVersion(ContractType, *Version).String(): {
EVM: common.FromHex(gobindings.{{.ContractType}}MetaData.Bin),
{{- if .ZkSyncBytecodeSymbol}}
ZkSyncVM: {{if .ZkSyncBytecodeUseGobindings}}gobindings{{else}}zkbindings{{end}}.{{.ZkSyncBytecodeSymbol}},
{{- end}}
},
},
})
Expand Down
34 changes: 21 additions & 13 deletions tools/operations-gen/internal/families/evm/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,22 @@ import (
// ---- Template data (EVM-specific) ----

type templateData struct {
PackageName string
PackageNameHyphen string
ContractType string
Version string
GobindingsImport string
NeedsBigInt bool
HasWriteOps bool
OmitDeploy bool
Constructor *constructorData
StructDefs []structDefData
ArgStructs []argStructData
Operations []OperationData
ContractMethods []contractMethodData
PackageName string
PackageNameHyphen string
ContractType string
Version string
GobindingsImport string
ZkSyncBytecodeSymbol string
ZkSyncBytecodeImport string
ZkSyncBytecodeUseGobindings bool
NeedsBigInt bool
HasWriteOps bool
OmitDeploy bool
Constructor *constructorData
StructDefs []structDefData
ArgStructs []argStructData
Operations []OperationData
ContractMethods []contractMethodData
}

type constructorData struct {
Expand Down Expand Up @@ -92,6 +95,11 @@ func prepareTemplateData(info *ContractInfo) templateData {
NeedsBigInt: ChecksNeedsBigInt(info),
OmitDeploy: info.OmitDeploy,
}
if info.ZkSync != nil {
data.ZkSyncBytecodeSymbol = info.ZkSync.BytecodeSymbol
data.ZkSyncBytecodeImport = info.ZkSync.BytecodePackage
data.ZkSyncBytecodeUseGobindings = info.ZkSync.BytecodePackage == info.GobindingsPackage
}

if info.Constructor != nil {
data.Constructor = &constructorData{
Expand Down
4 changes: 4 additions & 0 deletions tools/operations-gen/internal/families/evm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type EvmContractConfig struct {
PackageName string `yaml:"package_name,omitempty"` // Optional: override package name
OmitDeploy bool `yaml:"omit_deploy,omitempty"` // Optional: skip Deploy operation
GobindingsPackage string `yaml:"gobindings_package"` // Optional: override the derived gobindings import path or relative filesystem path for this contract.
ZkSyncBytecode ZkSyncBytecodeRef `yaml:"zksync_bytecode,omitempty"`
Functions []EvmFunctionConfig `yaml:"functions"`
ConfigDir string `yaml:"-"`
}
Expand All @@ -18,6 +19,9 @@ type EvmInputConfig struct {
// Contract packages default to:
// <gobindings_package>/<version_path>/<package_name>
GobindingsPackage string `yaml:"gobindings_package"`
// ZkSyncBindingsPackage is the default Go import path for zkSync VM deploy bytecode.
// Used when a contract sets zksync_bytecode to a symbol only.
ZkSyncBindingsPackage string `yaml:"zksync_bindings_package,omitempty"`
}

// EvmFunctionConfig selects a contract function and assigns its access control.
Expand Down
22 changes: 22 additions & 0 deletions tools/operations-gen/internal/families/evm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ContractInfo struct {
Version string
PackageName string
GobindingsPackage string
ZkSync *ZkSyncContractInfo
OutputPath string
OmitDeploy bool
Constructor *FunctionInfo
Expand All @@ -37,6 +38,12 @@ type ContractInfo struct {
StructDefs map[string]*structDef
}

// ZkSyncContractInfo holds resolved zkSync VM deploy bytecode for code generation.
type ZkSyncContractInfo struct {
BytecodePackage string
BytecodeSymbol string
}

type structDef struct {
Name string
Fields []ParameterInfo
Expand Down Expand Up @@ -98,6 +105,15 @@ func extractContractInfo(cfg EvmContractConfig, input EvmInputConfig, output Evm
}
cfg.GobindingsPackage = resolvedGobindingsPackage

if cfg.OmitDeploy && !cfg.ZkSyncBytecode.IsZero() {
return nil, fmt.Errorf("contract %q: zksync_bytecode cannot be set when omit_deploy is true", cfg.Name)
}

zkSyncPackage, zkSyncSymbol, err := resolveZkSyncBytecode(cfg, input, cfg.GobindingsPackage)
if err != nil {
return nil, err
}

parsedAbi, err := ReadABI(cfg)
if err != nil {
return nil, err
Expand All @@ -113,6 +129,12 @@ func extractContractInfo(cfg EvmContractConfig, input EvmInputConfig, output Evm
Functions: make(map[string]*FunctionInfo),
StructDefs: make(map[string]*structDef),
}
if zkSyncSymbol != "" {
info.ZkSync = &ZkSyncContractInfo{
BytecodePackage: zkSyncPackage,
BytecodeSymbol: zkSyncSymbol,
}
}

extractConstructor(info, parsedAbi)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ func TestGenerateFeeQuoter(t *testing.T) {
runGoldenGenerationTest(t, "operations_gen_fee_quoter.yaml", "fee_quoter.golden.go")
}

// TestGenerateLinkTokenWithZkSyncBindingsPackage verifies generation when
// input.zksync_bindings_package and contract zksync_bytecode are both set.
func TestGenerateLinkTokenWithZkSyncBindingsPackage(t *testing.T) {
t.Parallel()
runGoldenGenerationTest(t, "operations_gen_link_token_zksync_config.yaml", "link_token_zksync.golden.go")
}

func runGoldenGenerationTest(t *testing.T, configFileName string, goldenFileName string) {
t.Helper()

Expand Down
48 changes: 48 additions & 0 deletions tools/operations-gen/internal/families/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,51 @@ contracts:
require.Contains(t, string(got), `gobindings "`+wantGobindingsPackage+`"`)
require.NotContains(t, string(got), `gobindings "./testdata/evm/gobindings`)
}

func TestGenerateResolvesRelativeZkSyncBindingsPackage(t *testing.T) {
t.Parallel()

const wantZkSyncPackage = "github.com/smartcontractkit/chainlink-deployments-framework/tools/operations-gen/testdata/evm/zksync_bindings"
config := `version: "1.0.0"
chain_family: evm

input:
gobindings_package: "github.com/smartcontractkit/chainlink-deployments-framework/tools/operations-gen/testdata/evm/gobindings"
zksync_bindings_package: "./testdata/evm/zksync_bindings"

output:
base_path: "."

contracts:
- contract_name: ManyChainMultiSig
version: "1.0.0"
package_name: many_chain_multi_sig
zksync_bytecode: ManyChainMultiSigZkBytecode
functions:
- name: owner
access: public
`

var cfg core.Config
require.NoError(t, yaml.Unmarshal([]byte(config), &cfg), "parsing config")

moduleDir, err := filepath.Abs(filepath.Join("..", "..", ".."))
require.NoError(t, err)
tmpDir := t.TempDir()
outputBasePath, err := filepath.Rel(moduleDir, tmpDir)
require.NoError(t, err)
cfg.ConfigDir = moduleDir
cfg.Output = mustYAMLNode(t, evm.EvmOutputConfig{BasePath: outputBasePath})

tmpl, err := generate.LoadTemplate("evm")
require.NoError(t, err, "loadTemplate")

require.NoError(t, evm.Handler{}.Generate(cfg, tmpl), "Generate")

outputPath := core.ContractOutputPath(tmpDir, core.VersionToPath("1.0.0"), "many_chain_multi_sig")
got, err := os.ReadFile(outputPath)
require.NoError(t, err, "reading generated file %s", outputPath)

require.Contains(t, string(got), `zkbindings "`+wantZkSyncPackage+`"`)
require.Contains(t, string(got), "ZkSyncVM: zkbindings.ManyChainMultiSigZkBytecode")
}
85 changes: 85 additions & 0 deletions tools/operations-gen/internal/families/evm/zksync_bytecode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package evm

import (
"errors"
"fmt"

"gopkg.in/yaml.v3"
)

// ZkSyncBytecodeRef identifies zkSync VM deploy bytecode for a contract.
// YAML accepts a shorthand symbol string or {package, symbol}.
type ZkSyncBytecodeRef struct {
Package string
Symbol string
}

func (z *ZkSyncBytecodeRef) UnmarshalYAML(value *yaml.Node) error {
*z = ZkSyncBytecodeRef{}
if value == nil {
return nil
}

switch value.Kind {
case yaml.ScalarNode:
if isYAMLNullScalar(value) {
return nil
}
z.Symbol = value.Value

return nil
case yaml.MappingNode:
var raw struct {
Package string `yaml:"package"`
Symbol string `yaml:"symbol"`
}
if err := value.Decode(&raw); err != nil {
return err
}
z.Package = raw.Package
z.Symbol = raw.Symbol
if z.Symbol == "" {
return errors.New("zksync_bytecode mapping requires symbol")
}

return nil
case yaml.DocumentNode, yaml.SequenceNode, yaml.AliasNode:
return fmt.Errorf("zksync_bytecode must be a string or mapping, got %v", value.ShortTag())
}

return fmt.Errorf("zksync_bytecode must be a string or mapping, got %v", value.ShortTag())
}

func isYAMLNullScalar(value *yaml.Node) bool {
return value.Tag == "!!null" || value.Value == "" || value.Value == "~"
}

func (z ZkSyncBytecodeRef) IsZero() bool {
return z.Symbol == ""
}

func resolveZkSyncBytecode(
cfg EvmContractConfig,
input EvmInputConfig,
gobindingsPackage string,
) (packagePath string, symbol string, err error) {
if cfg.ZkSyncBytecode.IsZero() {
return "", "", nil
}

symbol = cfg.ZkSyncBytecode.Symbol
pkgPath := cfg.ZkSyncBytecode.Package
if pkgPath == "" {
pkgPath = input.ZkSyncBindingsPackage
}
if pkgPath == "" {
return gobindingsPackage, symbol, nil
}

resolved, err := resolveGobindingsImportPath(pkgPath, cfg.ConfigDir)
if err != nil {
return "", "", fmt.Errorf("resolve zksync_bytecode package for contract %q: %w", cfg.Name, err)
}

return resolved, symbol, nil
}
Loading
Loading