diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index 39c00a14ae2..c5669c49bd6 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -383,6 +383,14 @@ export const SIDEBAR: Partial> = { "cre/guides/workflow/using-solana-client/onchain-write-go", ], }, + { + title: "Forwarder Directory", + url: "cre/guides/workflow/using-solana-client/forwarder-directory", + highlightAsCurrent: [ + "cre/guides/workflow/using-solana-client/forwarder-directory-ts", + "cre/guides/workflow/using-solana-client/forwarder-directory-go", + ], + }, ], }, { diff --git a/src/content/cre/guides/workflow/using-solana-client/forwarder-directory-go.mdx b/src/content/cre/guides/workflow/using-solana-client/forwarder-directory-go.mdx new file mode 100644 index 00000000000..f80db8a89a6 --- /dev/null +++ b/src/content/cre/guides/workflow/using-solana-client/forwarder-directory-go.mdx @@ -0,0 +1,63 @@ +--- +section: cre +title: "Solana Forwarder Directory" +sdkLang: "go" +pageId: "solana-forwarder-directory" +date: Last Modified +metadata: + description: "Find Solana forwarder program and state account addresses for CRE workflows on simulation and production networks." + datePublished: "2026-07-15" + lastModified: "2026-07-15" +--- + +import { Aside, CopyText } from "@components" + +This page lists forwarder program and state account addresses for CRE Solana workflows. Solana uses a different forwarder +model than EVM: instead of a single contract address, each forwarder deployment has a **program ID** and a **state +account**. Your workflow config needs both values. + + + + + +## How to use this page + +Your workflow config (`config.staging.json` / `config.production.json`) includes `forwarderProgramId` and +`forwarderState` fields. Use the simulation addresses for `cre workflow simulate` and the production addresses when +deploying to the DON. Learn more: [Writing to Solana](/cre/guides/workflow/using-solana-client/onchain-write-go). + +{/* prettier-ignore */} + + +## Simulation (mock forwarder) + +These addresses are baked into the CLI and used automatically by `cre workflow simulate`. Use these **only** during local +development and testing. + +| Network | Mock Forwarder Program ID | Mock Forwarder State Account | +| -------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Solana Devnet | | | +| Solana Mainnet | | | + +## Production (Keystone Forwarder) + +For production deployments, use these live Keystone Forwarder addresses: + +| Network | Keystone Forwarder Program ID | Keystone Forwarder State Account | +| -------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Solana Devnet | | | +| Solana Mainnet | | | + +The workflow derives `forwarderAuthority` at runtime from `[]byte("forwarder")`, forwarder state bytes, and receiver +program bytes under the `forwarderProgramId`. diff --git a/src/content/cre/guides/workflow/using-solana-client/forwarder-directory-ts.mdx b/src/content/cre/guides/workflow/using-solana-client/forwarder-directory-ts.mdx new file mode 100644 index 00000000000..ea603823356 --- /dev/null +++ b/src/content/cre/guides/workflow/using-solana-client/forwarder-directory-ts.mdx @@ -0,0 +1,63 @@ +--- +section: cre +title: "Solana Forwarder Directory" +sdkLang: "ts" +pageId: "solana-forwarder-directory" +date: Last Modified +metadata: + description: "Find Solana forwarder program and state account addresses for CRE workflows on simulation and production networks." + datePublished: "2026-07-15" + lastModified: "2026-07-15" +--- + +import { Aside, CopyText } from "@components" + +This page lists forwarder program and state account addresses for CRE Solana workflows. Solana uses a different forwarder +model than EVM: instead of a single contract address, each forwarder deployment has a **program ID** and a **state +account**. Your workflow config needs both values. + + + + + +## How to use this page + +Your workflow config (`config.staging.json` / `config.production.json`) includes `forwarderProgramId` and +`forwarderState` fields. Use the simulation addresses for `cre workflow simulate` and the production addresses when +deploying to the DON. Learn more: [Writing to Solana](/cre/guides/workflow/using-solana-client/onchain-write-ts). + +{/* prettier-ignore */} + + +## Simulation (mock forwarder) + +These addresses are baked into the CLI and used automatically by `cre workflow simulate`. Use these **only** during local +development and testing. + +| Network | Mock Forwarder Program ID | Mock Forwarder State Account | +| -------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Solana Devnet | | | +| Solana Mainnet | | | + +## Production (Keystone Forwarder) + +For production deployments, use these live Keystone Forwarder addresses: + +| Network | Keystone Forwarder Program ID | Keystone Forwarder State Account | +| -------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Solana Devnet | | | +| Solana Mainnet | | | + +The workflow derives `forwarderAuthority` at runtime from `["forwarder", forwarderState, receiverProgram]` under the +`forwarderProgramId`. diff --git a/src/content/cre/guides/workflow/using-solana-client/generating-bindings-go.mdx b/src/content/cre/guides/workflow/using-solana-client/generating-bindings-go.mdx index 69cab0842f2..b1b6e8e8d33 100644 --- a/src/content/cre/guides/workflow/using-solana-client/generating-bindings-go.mdx +++ b/src/content/cre/guides/workflow/using-solana-client/generating-bindings-go.mdx @@ -78,11 +78,11 @@ For each struct in your IDL's `types` section, the generator creates a `WriteRep **Example: A minimal `DataStorage` IDL** -The following IDL is enough to generate a `WriteReportFromUserData()` helper. Save it as `contracts/solana/src/idl/data_storage.json`. You can run this example without deploying your own Anchor program: the program ID in the IDL is used as the write receiver, and simulation uses the Solana Devnet mock forwarder built into the CLI. +The following IDL is enough to generate a `WriteReportFromUserData()` helper. Save it as `contracts/solana/src/idl/data_storage.json`. ```json { - "address": "ECL8142j2YQAvs9R9geSsRnkVH2wLEi7soJCRyJ74cfL", + "address": "64Q1Xu7AEbc2xgaN21zZU1y1mkaLJNnP5cHFkthGuW31", "metadata": { "name": "data_storage", "version": "0.1.0", @@ -118,150 +118,38 @@ The following IDL is enough to generate a `WriteReportFromUserData()` helper. Sa } ``` -1. Generate the bindings from your project root. +After running `cre generate-bindings solana`, use the generated `data_storage` package in your workflow: - ```bash - cre generate-bindings solana - ``` +```go +import ( + solanago "github.com/gagliardetto/solana-go" + solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana" - This creates `contracts/solana/src/generated/data_storage/` with a `UserData` struct and `WriteReportFromUserData(runtime, input, remainingAccounts, computeConfig)`. + "/contracts/solana/src/generated/data_storage" +) -1. Sync dependencies. - - ```bash - go mod tidy - ``` - -1. Add Solana Devnet to `project.yaml` under your simulation target. - - ```yaml - staging-settings: - rpcs: - - chain-name: solana-devnet - url: https://api.devnet.solana.com - ``` - -1. Save the following as `config.staging.json` in your workflow folder. - - These addresses are the Solana Devnet **mock forwarder** values used by `cre workflow simulate`. The `forwarderAuthority` PDA is derived from `["forwarder", forwarderState, receiverProgram]` under the mock forwarder program ID for the `DataStorage` receiver above. `reportState` is a placeholder writable account for this dry-run example. +// In your workflow handler... +client := &solana.Client{ChainSelector: config.ChainSelector} +ds, err := data_storage.NewDataStorage(client) +if err != nil { + return "", err +} - ```json - { - "schedule": "*/30 * * * * *", - "chainSelector": "16423721717087811551", - "forwarderState": "5Tipz3yhTBdVsDbaBxZkrp7Gjf3brGq5SKkxReefPMP7", - "forwarderAuthority": "5wxb2QHeB4iyN1mPfmsjdsaxoP4P9EmTU7zBzpp4UgVu", - "reportState": "11111111111111111111111111111111" - } - ``` +// remainingAccounts: Index 0 forwarderState, 1 forwarderAuthority, 2+ receiver accounts +accounts := []*solana.AccountMeta{ + {PublicKey: forwarderStatePk[:], IsWritable: false}, + {PublicKey: forwarderAuthPk[:], IsWritable: false}, + {PublicKey: reportStatePk[:], IsWritable: true}, +} -1. Save the following as `my-workflow/workflow.go`. - - 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 `` with your Go module name from `go.mod`. +// Pass the struct data directly — types are derived from the IDL +result, err := ds.WriteReportFromUserData(runtime, data_storage.UserData{ + Key: "price", + Value: "500000", +}, accounts, &solana.ComputeConfig{ComputeLimit: 200_000}).Await() +``` - ```go - // my-workflow/workflow.go - package main - - import ( - "log/slog" - - solanago "github.com/gagliardetto/solana-go" - solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana" - "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron" - "github.com/smartcontractkit/cre-sdk-go/cre" - - "/contracts/solana/src/generated/data_storage" - ) - - type Config struct { - Schedule string `json:"schedule"` - ChainSelector uint64 `json:"chainSelector,string"` - ForwarderState string `json:"forwarderState"` - ForwarderAuthority string `json:"forwarderAuthority"` - ReportState string `json:"reportState"` - } - - func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error) { - cronTrigger := cron.Trigger(&cron.Config{Schedule: config.Schedule}) - - return cre.Workflow[*Config]{ - cre.Handler(cronTrigger, onCronTrigger), - }, nil - } - - func onCronTrigger(config *Config, runtime cre.Runtime, _ *cron.Payload) (string, error) { - client := &solana.Client{ChainSelector: config.ChainSelector} - - // NewDataStorage uses the program ID baked into the IDL - ds, err := data_storage.NewDataStorage(client) - if err != nil { - return "", err - } - - forwarderStatePk := solanago.MustPublicKeyFromBase58(config.ForwarderState) - forwarderAuthPk := solanago.MustPublicKeyFromBase58(config.ForwarderAuthority) - reportStatePk := solanago.MustPublicKeyFromBase58(config.ReportState) - - // remainingAccounts layout required by the forwarder: - // Index 0: forwarderState (read-only) - // Index 1: forwarderAuthority PDA (read-only) - // Index 2+: receiver-specific accounts - accounts := []*solana.AccountMeta{ - {PublicKey: forwarderStatePk[:], IsWritable: false}, - {PublicKey: forwarderAuthPk[:], IsWritable: false}, - {PublicKey: reportStatePk[:], IsWritable: true}, - } - - runtime.Logger().Info("Submitting UserData to Solana DataStorage program") - - // Solana simulate requires a non-nil ComputeConfig with ComputeLimit > 0 - computeConfig := &solana.ComputeConfig{ComputeLimit: 200_000} - - result, err := ds.WriteReportFromUserData(runtime, data_storage.UserData{ - Key: "price", - Value: "500000", - }, accounts, computeConfig).Await() - if err != nil { - return "", err - } - - runtime.Logger().Info("Write completed", - "txStatus", result.GetTxStatus().String(), - "errorMessage", result.GetErrorMessage(), - ) - return "ok", nil - } - ``` - -1. Set `CRE_SOLANA_PRIVATE_KEY` in your project `.env`, then run the simulation. - - Solana simulation requires a 64-byte base58 keypair even for dry-run (no `--broadcast`). The transmitter account derived from that key must exist and be funded on Solana Devnet. Generate a key with `solana-keygen new`, fund it with `solana airdrop` (or a transfer), and put the keypair in `.env`: - - ```bash - CRE_SOLANA_PRIVATE_KEY= - ``` - - Then from your project root: - - ```bash - cre workflow simulate my-workflow --target staging-settings - ``` - - Expected output (dry-run against the mock forwarder). This example uses a placeholder receiver program ID, so the - write path completes through the mock forwarder and the CPI into the receiver returns `Custom:2007`. That confirms - bindings, encoding, and report submission worked: - - ``` - ✓ Workflow compiled - [SIMULATION] Simulator Initialized - [SIMULATION] Running trigger trigger=cron-trigger@1.0.0 - [USER LOG] msg="Submitting UserData to Solana DataStorage program" - [USER LOG] msg="Write completed" txStatus=TX_STATUS_FATAL errorMessage="map[InstructionError:[0 map[Custom:2007]]]" - - ✓ Workflow Simulation Result: - "ok" - ``` +For a full walkthrough including config, simulation, and production deployment, see [Writing to Solana](/cre/guides/workflow/using-solana-client/onchain-write-go).