Skip to content

Commit 340cea6

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 340cea6

File tree

5 files changed

+1190
-25
lines changed

5 files changed

+1190
-25
lines changed

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

0 commit comments

Comments
 (0)