Skip to content

Commit c142d01

Browse files
feat(operations-gen): add evm support
Add evm support for the operations-gen cli, basically ported the code from https://github.com/smartcontractkit/chainlink-ccip/blob/main/chains/evm/cmd/operations-gen/main.go and adapted it to the new design JIRA: https://smartcontract-it.atlassian.net/browse/CLD-1777
1 parent 6da1e79 commit c142d01

7 files changed

Lines changed: 1324 additions & 46 deletions

File tree

taskfiles/generate/Taskfile.yml

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,3 @@ tasks:
99
desc: Generate mocks for interfaces
1010
cmds:
1111
- mockery
12-
13-
operations:
14-
desc: Generate contract operation wrappers from a config file
15-
summary: |
16-
Generates type-safe Go operation wrappers for EVM (or other chain family) contracts.
17-
18-
Pass CONFIG variable with an absolute path, or a path relative to the repo root.
19-
Relative paths are resolved against the repo root before being passed to the generator.
20-
21-
Example:
22-
task generate:operations CONFIG=/path/to/operations_gen_config.yaml
23-
task generate:operations CONFIG=./path/from/repo/root/operations_gen_config.yaml
24-
vars:
25-
CONFIG: '{{.CONFIG}}'
26-
# If CONFIG is absolute use it as-is; otherwise treat it as relative to the repo root.
27-
# This avoids a dependency on `realpath` which is not available on all platforms.
28-
ABS_CONFIG:
29-
sh: |
30-
case "{{.CONFIG}}" in
31-
/*) printf '%s\n' "{{.CONFIG}}" ;;
32-
*) printf '%s\n' "{{.ROOT_DIR}}/{{.CONFIG}}" ;;
33-
esac
34-
cmds:
35-
- cd {{.ROOT_DIR}}/tools/operations-gen && go run . -config {{.ABS_CONFIG}}

tools/operations-gen/README.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# operations-gen
2+
3+
Generates type-safe Go operation wrappers for smart contracts from their ABIs.
4+
5+
## Usage
6+
7+
```bash
8+
go run . -config /path/to/operations_gen_config.yaml
9+
```
10+
11+
The `-config` path can be absolute or relative to the current working directory.
12+
13+
## Configuration
14+
15+
Create an `operations_gen_config.yaml` alongside your ABI/bytecode directories:
16+
17+
```yaml
18+
version: "1.0.0"
19+
chain_family: evm # Optional: defaults to "evm"
20+
21+
input:
22+
abi_base_path: "./abi" # Directory containing versioned ABI json files
23+
bytecode_base_path: "./bytecode" # Directory containing versioned bytecode .bin files
24+
25+
output:
26+
base_path: "." # Directory where generated operations/ folders are written
27+
28+
contracts:
29+
- contract_name: FeeQuoter
30+
version: "1.6.0"
31+
package_name: fee_quoter # Optional: override default package name
32+
abi_file: "fee_quoter.json" # Optional: override default ABI filename
33+
omit_deploy: false # Optional: skip Deploy operation generation
34+
functions:
35+
- name: updatePrices
36+
access: owner # Write op with MCMS support
37+
- name: getTokenPrice
38+
access: public # Read op (or public write op)
39+
```
40+
41+
### Top-level fields
42+
43+
| Field | Required | Description |
44+
| ------------------ | -------- | --------------------------------------------------------------------------------------------- |
45+
| `version` | Yes | Config schema version |
46+
| `chain_family` | No | Target chain family. Only `"evm"` is supported. Defaults to `"evm"`. |
47+
| `input.abi_base_path` | Yes | Directory containing versioned ABI files. Relative to the config file. |
48+
| `input.bytecode_base_path` | Yes | Directory containing versioned bytecode files. Relative to the config file. |
49+
| `output.base_path` | Yes | Root directory where generated files are written. Relative to the config file. |
50+
51+
### Contract fields
52+
53+
| Field | Required | Description |
54+
| --------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
55+
| `contract_name` | Yes | Contract name as it appears in the ABI (e.g. `FeeQuoter`) |
56+
| `version` | Yes | Semver version of the contract (e.g. `"1.6.0"`) |
57+
| `package_name` | No | Override the generated Go package name. Defaults to `snake_case(contract_name)`. |
58+
| `abi_file` | No | Override the ABI filename. Defaults to `{package_name}.json`. |
59+
| `version_path` | No | Override the directory path derived from the version. Defaults to `v{major}_{minor}_{patch}`. |
60+
| `omit_deploy` | No | Skip generation of the `Deploy` operation and bytecode constant. Defaults to `false`. |
61+
62+
### Function access control
63+
64+
| Value | Behaviour |
65+
| -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
66+
| `owner` | Generates a write operation gated by `OnlyOwner`, producing an MCMS-compatible transaction when the deployer key is not the owner. |
67+
| `public` | Generates a read operation (for `view`/`pure` functions) or an unrestricted write operation. |
68+
69+
## Input layout
70+
71+
The generator expects ABIs and bytecode under separate input roots:
72+
73+
```
74+
{input.abi_base_path}/
75+
v1_6_0/
76+
fee_quoter.json
77+
78+
{input.bytecode_base_path}/
79+
v1_6_0/
80+
fee_quoter.bin
81+
```
82+
83+
Version `1.6.0` maps to directory `v1_6_0`. Override with `version_path` if your layout differs.
84+
85+
## Output layout
86+
87+
Generated files are written to:
88+
89+
```
90+
{output.base_path}/
91+
v1_6_0/
92+
operations/
93+
fee_quoter/
94+
fee_quoter.go
95+
```
96+
97+
Each generated file contains:
98+
99+
- ABI and bytecode constants
100+
- A bound contract wrapper with typed methods
101+
- A `Deploy` operation (unless `omit_deploy: true`)
102+
- A typed write operation for each `access: owner` or writable `access: public` function
103+
- A typed read operation for each `view`/`pure` function
104+
105+
The generated code imports the runtime helpers from:
106+
107+
```
108+
github.com/smartcontractkit/chainlink-deployments-framework/chain/evm/operations/contract
109+
```
110+
111+
## Extending to new chain families
112+
113+
> Only `evm` is supported today. The steps below describe how to add support for a new family in the future.
114+
115+
The generator dispatches entirely by `chain_family`. Each family owns its own YAML contract schema, type mappings, template, and generation logic — nothing is shared between families at the code level.
116+
117+
To add a new chain family (e.g. `solana`):
118+
119+
1. Create `solana.go` with a `solanaHandler` struct implementing `ChainFamilyHandler`:
120+
121+
```go
122+
type ChainFamilyHandler interface {
123+
Generate(config Config, tmpl *template.Template) error
124+
}
125+
```
126+
127+
The handler receives the full `Config`. `Config.Contracts` is a single `yaml.Node` whose `Content` slice holds the per-contract nodes; each handler decodes its own chain-specific contract schema from those nodes.
128+
129+
2. Add `templates/solana/operations.tmpl` with chain-appropriate imports and method bodies.
130+
131+
3. Register the handler in `chainFamilies` in `main.go`:
132+
```go
133+
var chainFamilies = map[string]ChainFamilyHandler{
134+
"evm": evmHandler{},
135+
"solana": solanaHandler{},
136+
}
137+
```
138+
139+
No other changes to `main.go` are needed. Set `chain_family: solana` in your config to use it.

0 commit comments

Comments
 (0)