# Generating Contract Bindings
Source: https://docs.chain.link/cre/guides/workflow/using-evm-client/generating-bindings-go
Last Updated: 2026-03-17

> For the complete documentation index, see [llms.txt](/llms.txt).

To interact with a smart contract from your Go workflow, you first need to create **bindings**. Bindings are type-safe Go interfaces auto-generated from your contract's ABI. They provide a bridge between your Go code and the EVM.

> **NOTE: TypeScript support**
>
> `cre generate-bindings evm` also supports TypeScript as of CLI v1.3.0. See [Generating Contract Bindings
> (TypeScript)](/cre/guides/workflow/using-evm-client/generating-bindings-ts) for details.

How they work depends on whether you are reading from or writing to the chain:

- **For onchain reads**, bindings provide Go functions that directly mirror your contract's `view` and `pure` methods.
- **For onchain writes**, bindings provide powerful helper methods to ABI-encode your data structures, preparing them to be sent in a report to a [consumer contract](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts/).

This is a **one-time code generation step** performed using the CRE CLI.

## The generation process

The CRE CLI provides an automated binding generator that reads contract ABIs and creates corresponding Go packages.

The target language is **auto-detected** from your project files (presence of `go.mod` picks Go). You can also force a specific language with the `--language` flag:

```bash
cre generate-bindings evm --language go
```

### Step 1: Add your contract ABI

Place your contract ABI file into the `contracts/evm/src/abi/` directory. Two file formats are supported:

- **`*.abi`** — A raw JSON array of ABI entries, as produced by `solc` or extracted from a compiled artifact
- **`*.json`** — A compiled artifact file (Hardhat, Foundry, or similar) with a top-level `"abi"` field

For example, to generate bindings for a `PriceUpdater` contract, create either `contracts/evm/src/abi/PriceUpdater.abi` or `contracts/evm/src/abi/PriceUpdater.json`. Both formats can coexist in the same directory.

### Step 2: Generate the bindings

From your **project root**, run the binding generator:

```bash
cre generate-bindings evm
```

This command scans all `.abi` and `.json` files in `contracts/evm/src/abi/` and generates corresponding Go packages in `contracts/evm/src/generated/`. For each contract, two files are generated:

- `<ContractName>.go` — The main binding for interacting with the contract
- `<ContractName>_mock.go` — A mock implementation for testing your workflows without deploying contracts

## Using generated bindings

### For onchain reads

For `view` or `pure` functions, the generator creates a client with methods that you can call directly. These methods return a `Promise`, which you must `.Await()` to get the result after consensus.

**Example: A simple `Storage` contract**

Create `contracts/evm/src/abi/Storage.abi` with the following content. This contract is already deployed on Sepolia at `0xa17CF997C28FF154eDBae1422e6a50BeF23927F4` with an initial value of `22`, so you can run this example without deploying anything.

```json
[
  {
    "inputs": [{ "internalType": "uint256", "name": "initialValue", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "get",
    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "value",
    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  }
]
```

After running `cre generate-bindings evm`, use the generated `storage` package in your workflow. A Go project is split across two files — `main.go` is the WASM entry point (generated by `cre init` and unchanged), and your workflow logic lives in a separate file such as `workflow.go`. Replace `<project-name>` with your Go module name from `go.mod`.

```go
// my-workflow/workflow.go
package main

import (
    "log/slog"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm"
    "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron"
    "github.com/smartcontractkit/cre-sdk-go/cre"

    "<project-name>/contracts/evm/src/generated/storage"
)

type Config struct {
    ChainSelector  uint64 `json:"chainSelector"`
    StorageAddress string `json:"storageAddress"`
}

func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error) {
    cronTrigger := cron.Trigger(&cron.Config{Schedule: "*/30 * * * * *"})

    return cre.Workflow[*Config]{
        cre.Handler(cronTrigger, onCronTrigger),
    }, nil
}

func onCronTrigger(config *Config, runtime cre.Runtime, _ *cron.Payload) (string, error) {
    evmClient := &evm.Client{ChainSelector: config.ChainSelector}
    contractAddress := common.HexToAddress(config.StorageAddress)

    // Create a new contract instance using the generated bindings
    storageContract, err := storage.NewStorage(evmClient, contractAddress, nil)
    if err != nil {
        return "", err
    }

    // Call a read-only method — it returns a Promise, which you must .Await() to get the result
    // big.NewInt(-3) means the finalized block; use big.NewInt(-2) for latest
    value, err := storageContract.Get(runtime, big.NewInt(-3)).Await()
    if err != nil {
        return "", err
    }

    // value is already a *big.Int, ready to use
    runtime.Logger().Info("Storage value", "value", value.String())
    return value.String(), nil
}
```

`config.staging.json`:

```json
{
  "chainSelector": 16015286601757825753,
  "storageAddress": "0xa17CF997C28FF154eDBae1422e6a50BeF23927F4"
}
```

Run the simulation from your project root:

```bash
cre workflow simulate my-workflow
```

Expected output:

```
✓ Workflow compiled
[SIMULATION] Simulator Initialized
[SIMULATION] Running trigger trigger=cron-trigger@1.0.0
[USER LOG] msg="Storage value" value=22

✓ Workflow Simulation Result:
"22"
```

> **NOTE: Block number options**
>
> The `big.NewInt(-3)` parameter specifies reading from the finalized block. You can also use `big.NewInt(-2)` for the
> latest block, or use constants from the `go-ethereum/rpc` package for better readability. See [Block number
> options](/cre/guides/workflow/using-evm-client/onchain-read-go#block-number-options) for all available options.

### For onchain writes

For onchain writes, your goal is to send an ABI-encoded report to your [consumer contract](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts/). The binding generator creates helper methods that handle the entire process: creating the report, sending it for consensus, and delivering it to the chain.

#### Signaling the generator

To generate the necessary Go types and write helpers, your ABI must include at least one **`public` or `external` function that uses the data `struct` you want to send as a parameter**.

The generated helper method is named after the **input struct type**. For example, a struct named `PriceData` will generate a `WriteReportFromPriceData` helper.

> **NOTE: What about the proxy pattern?**
>
> Even if you are using the [proxy
> pattern](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts/#example-2-the-proxy-pattern)
> and the actual logic is in a different contract, you should generate bindings from an ABI that exposes the data
> structure you want to send. You can create a simple interface for this purpose if needed. The bindings will be used to
> call your *Proxy Contract*, but they need to be generated from an ABI that contains the correct function and struct
> definitions.

**Example: A `PriceUpdater` contract ABI**

This ABI contains a `PriceData` struct and a public `updatePrices` function. This is all the generator needs.

```solidity
// contracts/evm/src/PriceUpdater.sol
// This contract can be used purely to generate the bindings.
// The actual onchain logic can live elsewhere.
contract PriceUpdater {
  struct PriceData {
    uint256 ethPrice;
    uint256 btcPrice;
  }

  // The struct type (`PriceData`) determines the generated helper name.
  // The generator will create a `WriteReportFromPriceData` method.
  function updatePrices(PriceData memory) public {}
}
```

#### Using write bindings in a workflow

After running `cre generate-bindings`, you can use the generated `PriceUpdater` client to send a report. The workflow code will look like this:

```go
// Import the generated package for your contract, replacing "<project-name>" with your project's module name
import "<project-name>/contracts/evm/src/generated/price_updater"
import "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm"
import "github.com/ethereum/go-ethereum/common"
import "math/big"
import "fmt"

// In your workflow function...

// The address should be your PROXY contract's address.
contractAddress := common.HexToAddress(config.ProxyAddress)
evmClient := &evm.Client{ ChainSelector: config.ChainSelector }

// 1. Create a new contract instance using the generated bindings.
//    Even though it's called `price_updater`, it's configured with your proxy address.
priceUpdater, err := price_updater.NewPriceUpdater(evmClient, contractAddress, nil)
if err != nil { /* ... */ }

// 2. Instantiate the generated Go struct with your data.
reportData := price_updater.PriceData{
    EthPrice: big.NewInt(4000_000000),
    BtcPrice: big.NewInt(60000_000000),
}

// 3. Call the generated WriteReportFrom<StructName> method on the contract instance.
//    This method name is derived from the input struct of your contract's function.
writePromise := priceUpdater.WriteReportFromPriceData(runtime, reportData, nil)

// 4. Await the promise to confirm the transaction has been mined.
resp, err := writePromise.Await()
if err != nil {
    return nil, fmt.Errorf("WriteReport await failed: %w", err)
}

// 5. The response contains the transaction hash.
logger := runtime.Logger()
logger.Info("Write report transaction succeeded", "txHash", common.BytesToHash(resp.TxHash).Hex())
```

> **NOTE: What does the WriteReport helper do?**
>
> The `WriteReportFromPriceData` method automatically handles the entire onchain write process for you:

1. It ABI-encodes the `PriceData` struct into a `bytes` payload.
2. It calls `runtime.GenerateReport()` to create a secure, signed report from the DON.
3. It calls the EVM Client's `WriteReport` method to submit this report to your consumer contract.
4. It creates and returns a `Promise` that resolves with the final transaction details, including the transaction hash, once the transaction is confirmed.

### For event logs

The binding generator also creates powerful helpers for interacting with your contract's events. You can easily trigger a workflow when an event is emitted and decode the event data into a type-safe Go struct.

**Example: A contract with a `UserAdded` event**

```solidity
contract UserDirectory {
  event UserAdded(address indexed userAddress, string userName);

  function addUser(string calldata userName) external {
    emit UserAdded(msg.sender, userName);
  }
}
```

#### Triggering and Decoding Events

After generating bindings for the `UserDirectory` ABI, you can use the helpers to create a trigger and decode the logs in your handler.

```go
import (
    "log/slog"
    "<project-name>/contracts/evm/src/generated/user_directory" // Replace "<project-name>" with your project's module name
    "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm"
    "github.com/smartcontractkit/cre-sdk-go/cre"
)

// In InitWorkflow, create an instance of the contract binding and use it
// to generate a trigger for the "UserAdded" event.
func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error) {
    // ...
    userDirectory, err := user_directory.NewUserDirectory(evmClient, contractAddress, nil)
    if err != nil { /* ... */ }

    // Use the generated helper to create a trigger for the UserAdded event.
    // Set confidence to evm.ConfidenceLevel_CONFIDENCE_LEVEL_FINALIZED to only trigger on finalized blocks.
    // The last argument (filters) is nil to listen for all UserAdded events.
    userAddedTrigger, err := userDirectory.LogTriggerUserAddedLog(chainSelector, evm.ConfidenceLevel_CONFIDENCE_LEVEL_FINALIZED, nil)
    if err != nil { /* ... */ }

    return cre.Workflow[*Config]{
        cre.Handler(
            userAddedTrigger,
            onUserAdded,
        ),
    }, nil
}


// The handler function receives the raw event log.
func onUserAdded(config *Config, runtime cre.Runtime, log *evm.Log) (string, error) {
    logger := runtime.Logger()
    // You must re-create the contract instance to access the decoder.
    userDirectory, err := user_directory.NewUserDirectory(evmClient, contractAddress, nil)
    if err != nil { /* ... */ }

    // Use the generated Codec to decode the raw log into a typed Go struct.
    decodedLog, err := userDirectory.Codec.DecodeUserAdded(log)
    if err != nil {
        return "", fmt.Errorf("failed to decode log: %w", err)
    }

    logger.Info("New user added!", "address", decodedLog.UserAddress, "name", decodedLog.UserName)
    return "ok", nil
}
```

## What the CLI generates

The generator creates a Go package for each ABI file, placed in `contracts/evm/src/generated/<contractname>/`. Two files are generated:

- `<ContractName>.go` — The main binding
- `<ContractName>_mock.go` — A mock implementation for testing (excluded from WASM builds via `//go:build !wasip1`)

What's inside each file depends on your ABI:

- **For all contracts**:
  - A `<ContractName>Codec` interface with `Encode<Method>MethodCall()` and `Decode<Method>MethodOutput()` methods for low-level encoding and decoding.
  - A `<ContractName>` struct and `New<ContractName>(client, address, options)` constructor.
  - A `<ContractName>Mock` struct with a settable function field for each `view`/`pure` method (e.g., `Get func() (*big.Int, error)`), and a `New<ContractName>Mock(address, evmMockClient)` constructor.
- **For onchain reads** (each `view`/`pure` function):
  - A method on the contract struct (e.g., `Get(runtime, blockNumber)`) that returns a `cre.Promise[T]` you `.Await()`.
- **For onchain writes** (each non-view function with struct arguments):
  - A `WriteReportFrom<StructName>(runtime, data, gasConfig)` method that handles ABI encoding, report generation, and submission in one step.
- **For events** (each `event` definition):
  - A **Go struct** for the decoded event data (e.g., `UserAdded`).
  - A `Decode<EventName>` method on the `Codec` to parse raw log data into that struct.
  - A `LogTrigger<EventName>Log` method on the contract struct to create a workflow trigger.
  - A `FilterLogs<EventName>` method to query historical logs for that event.

## Using mock bindings for testing

The `<ContractName>_mock.go` files allow you to test your workflows without deploying or interacting with real contracts. Each mock struct provides:

- **Test-friendly constructor**: `New<ContractName>Mock(address, evmMockClient)` creates a mock instance
- **Mockable methods**: Set custom function implementations for each contract `view`/`pure` function
- **Type safety**: The same input/output types as the real binding

### Complete example: Testing a workflow with mocks

Let's say you have a workflow in `my-workflow/workflow.go` that reads from a `Storage` contract. Create a test file named `workflow_test.go` in the same directory.

> **NOTE: Adapt to Your Workflow**
>
> This example demonstrates the mock setup pattern. The `Config` and `EvmConfig` types shown are examples — you'll need
> to define types that match your workflow's structure. The key concepts (creating mocks, setting behavior) remain the
> same regardless of your specific config structure.

```go
// File: my-workflow/workflow_test.go
package main

import (
    "math/big"
    "testing"

    "github.com/ethereum/go-ethereum/common"
    "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm"
    evmmock "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm/mock"
    "github.com/stretchr/testify/require"

    "your-project/contracts/evm/src/generated/storage"
)

// Define your config types in the test file to match your workflow's structure.
// Note: your workflow.go likely has //go:build wasip1 (for WASM compilation),
// which means those types aren't available when running regular Go tests.
// So you need to redefine them here in your test file.
type EvmConfig struct {
    StorageAddress string `json:"storageAddress"`
    ChainName      string `json:"chainName"`
}

type Config struct {
    Evms []EvmConfig `json:"evms"`
}

func TestStorageRead(t *testing.T) {
    // 1. Set up your config
    config := &Config{
        Evms: []EvmConfig{
            {
                StorageAddress: "0xa17CF997C28FF154eDBae1422e6a50BeF23927F4",
                ChainName:      "ethereum-testnet-sepolia",
            },
        },
    }

    // 2. Create a mock EVM client
    chainSelector := uint64(evm.EthereumTestnetSepolia)
    evmMock, err := evmmock.NewClientCapability(chainSelector, t)
    require.NoError(t, err)

    // 3. Create a mock Storage contract and set up mock behavior
    storageAddress := common.HexToAddress(config.Evms[0].StorageAddress)
    storageMock := storage.NewStorageMock(storageAddress, evmMock)

    // 4. Mock the Get() function to return a controlled value
    storageMock.Get = func() (*big.Int, error) {
        return big.NewInt(42), nil
    }

    // 5. Now when your workflow code creates a Storage contract with this evmMock,
    //    it will automatically use the mocked Get() function.
    //    The mock is registered with the evmMock, so any contract at this address
    //    will use the mock behavior you defined.

    // In a real test, you would call your workflow function here and verify results.
    // Example:
    // result, err := onCronTrigger(config, runtime, &cron.Payload{})
    // require.NoError(t, err)
    // require.Equal(t, big.NewInt(42), result.StorageValue)

    // For this demo, we just verify the mock was set up
    require.NotNil(t, storageMock)
    t.Logf("Mock set up successfully - Get() will return 42")
}
```

### Running your tests

From your project root, run:

```bash
# Test a specific workflow
go test ./my-workflow

# Test with verbose output (shows t.Logf messages)
go test -v ./my-workflow

# Test all workflows in your project
go test ./...
```

**Expected output with `-v` flag:**

```bash
=== RUN   TestStorageRead
    workflow_test.go:55: Mock Storage contract set up at 0xa17CF997C28FF154eDBae1422e6a50BeF23927F4
    workflow_test.go:56: Mock set up successfully - Get() will return 42
--- PASS: TestStorageRead (0.00s)
PASS
ok      your-project/my-workflow      0.257s
```

The test passes, confirming your mock contract is set up correctly. In a real workflow test, you would call your workflow function and verify it produces the expected results using the mocked contract.

### Best practices for workflow testing

1. **Name test files correctly**: Use `<name>_test.go` (e.g., `workflow_test.go`) and place them in your workflow directory
2. **Test function naming**: Start test functions with `Test` (e.g., `TestMyWorkflow`, `TestCronTrigger`)
3. **Mock all external dependencies**: Use mock contracts for EVM calls and mock HTTP clients for API requests
4. **Test different scenarios**: Create separate test functions for success cases, error cases, and edge cases

### Complete reference example

For a comprehensive example showing how to test workflows with multiple triggers (cron, HTTP, EVM log) and multiple mock contracts, see the Custom Data Feed demo workflow's `workflow_test.go` file.

To generate this example:

1. Run `cre init` from your project directory
2. Select **Golang** as your language
3. Choose the **"Custom data feed: Updating on-chain data periodically using offchain API data"** template
4. After initialization completes, examine the generated `workflow_test.go` file in your workflow directory

This generated test file demonstrates real-world patterns for testing complex workflows with multiple capabilities and mock contracts.

## Best practices

1. **Regenerate when needed**: Re-run the generator if you update your contract ABIs.
2. **Handle errors**: Always check for errors at each step.
3. **Organize ABIs**: Keep your ABI files clearly named in the `contracts/evm/src/abi/` directory.
4. **Use mocks in tests**: Leverage the generated mock bindings to test your workflows in isolation without needing deployed contracts.

## Where to go next

Now that you know how to generate bindings, you can use them to [read data from](/cre/guides/workflow/using-evm-client/onchain-read) or [write data to](/cre/guides/workflow/using-evm-client/onchain-write/overview) your contracts, or [trigger workflows from events](/cre/guides/workflow/using-triggers/evm-log-trigger).