diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index 988f6966d48..5e969be916e 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -778,6 +778,11 @@ export const SIDEBAR: Partial> = { url: "cre/reference/clf-migration", highlightAsCurrent: ["cre/reference/clf-migration-ts", "cre/reference/clf-migration-go"], }, + { + title: "Migrate from Chainlink VRF", + url: "cre/reference/vrf-migration", + highlightAsCurrent: ["cre/reference/vrf-migration-ts", "cre/reference/vrf-migration-go"], + }, ], }, ], diff --git a/src/content/cre/llms-full-go.txt b/src/content/cre/llms-full-go.txt index 32ee8215927..0365fcfad2a 100644 --- a/src/content/cre/llms-full-go.txt +++ b/src/content/cre/llms-full-go.txt @@ -21928,6 +21928,241 @@ This section provides a reference for the built-in trigger capabilities of the C --- +# Migrate from Chainlink VRF to Chainlink CRE +Source: https://docs.chain.link/cre/reference/vrf-migration-go +Last Updated: 2026-07-23 + + + +For new randomness use cases, use the [Chainlink Runtime Environment (CRE)](/cre). Where [Chainlink VRF](/vrf) delivers a random number and nothing else, CRE generates randomness as one step inside a workflow that can also read onchain state, call APIs, run your business logic, and write results to one or more chains — all in a single deployment. + +This guide maps a VRF integration to a CRE workflow: how CRE generates randomness, how that randomness is secured, and how to move the request/fulfill contract flow into a workflow. + +## How VRF and CRE randomness differ + +In **VRF**, your consumer contract inherits `VRFConsumerBaseV2Plus` and calls `requestRandomWords()` on the VRF Coordinator. A Chainlink node generates a random value off-chain together with an ECVRF proof, submits it to the Coordinator, and the Coordinator verifies the proof onchain before calling your `fulfillRandomWords()` callback with the verified random words. Randomness is the entire product; any surrounding logic lives in your contract or an offchain service. + +In **CRE**, the unit of execution is a [**workflow**](/cre/key-terms#workflow) — a Go (or TypeScript) project compiled to WebAssembly and run across a [Decentralized Oracle Network (DON)](/cre/key-terms#decentralized-oracle-network-don). A [**trigger**](/cre/key-terms#trigger) (cron, HTTP, or onchain log) starts the workflow, the handler calls the runtime's randomness function, and the result is written onchain as a DON-signed report to any contract implementing `IReceiver`. Randomness is one [capability](/cre/key-terms#capability) among many the handler can call. + +## How CRE randomness works and how it is secured + +CRE randomness is **consensus-derived**. Every node in the DON runs the same workflow binary and, for a given execution, derives the same per-execution seed. Calling `runtime.Rand()` produces the **same sequence of values on every node**, so the DON can reach [consensus](/cre/concepts/consensus-computing) on a single result. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-go) for the full behavior. + +What that gives you, and how each part is checkable: + +- **No single node chooses the number.** The value is fixed by the shared seed and reproduced independently by every node. A result is only produced if a Byzantine-fault-tolerant supermajority of the DON agrees on it, so no individual node operator can select or bias the outcome. +- **The result reaches the chain as a DON-signed report.** CRE writes the result through the [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-go); a consumer implementing `IReceiver` only accepts reports carrying a valid signature from the authorized DON. This produces a tamper-evident, onchain trail of every delivered value. +- **The randomness code is open source and auditable.** The seed derivation and the generator are part of the public SDK and runtime. Anyone can read exactly how a value is produced rather than trusting a description of it. +- **Every result is bound to an identified workflow build.** The `workflowID` — a hash of the compiled workflow binary plus its config — is registered onchain and included in every signed report. With Go reproducible builds, anyone with the source can rebuild the binary, recompute that hash, and confirm it matches the workflow registered onchain — binding each delivered value to the exact code that produced it. See [Verifying Workflows](/cre/guides/operations/verifying-workflows-go) for how to check a workflow's identity and reproduce its build. +- **Executions are inspectable by your team.** The [CRE dashboard](/cre/guides/operations/monitoring-workflows) shows each execution's handler logs, per-step timings, consensus outcome, and the resulting onchain transaction — for your own operational audit and incident review. + + + +## Chain support + +CRE runs on a broad set of networks. For the canonical, up-to-date list, see [Supported Networks](/cre/supported-networks-go). + +## Terminology + +| Chainlink VRF | Chainlink CRE | +| :---------------------------------------------- | :------------------------------------------------------------------------------------------------ | +| `VRFConsumerBaseV2Plus` consumer contract | `IReceiver` consumer contract (use `ReceiverTemplate`) | +| `requestRandomWords()` from a consumer | A trigger (EVM Log, Cron, or HTTP) starts the workflow | +| VRF Coordinator | Workflow DON + [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-go) | +| ECVRF proof verified onchain by the Coordinator | DON consensus + DON-signed report accepted by `IReceiver` | +| `fulfillRandomWords(requestId, randomWords)` | `_onReport(metadata, payload)` on the receiver | +| Off-chain proof generation by a single node | `runtime.Rand()` inside the CRE WASM runtime, agreed by the DON | +| `keyHash` / gas lane | Chain selector + forwarder address per chain | +| LINK/native subscription or direct funding | CRE service model — no per-request subscription to fund | + +## Triggers + +VRF has one entry path: a contract calling `requestRandomWords()`. CRE makes the [trigger](/cre/guides/workflow/using-triggers/overview) first-class, which also determines how predictable the per-execution seed is. + +### Onchain events + +The direct replacement for the VRF request path. Instead of your contract calling a coordinator, the DON watches for an event your contract emits and fires the handler with the decoded event data. + +```go +evmClient := evm.Client(CHAIN_SELECTOR) +trigger := evmClient.LogTrigger(&evm.LogTriggerRequest{ + Addresses: [][]byte{/* contract address */}, + Topics: [][]byte{/* event signature hash */}, +}) +``` + +### Time-based + +Use a [cron trigger](/cre/guides/workflow/using-triggers/cron-trigger-go) for scheduled draws (periodic raffles, sampling) with no onchain request needed. + +```go +trigger := cron.Trigger(&cron.Config{Schedule: "*/5 * * * *"}) // every 5 min +``` + +### HTTP triggers + +For draws requested by an offchain application. The workflow exposes an HTTPS endpoint; callers POST a payload signed with an authorized EVM key. See the [HTTP trigger](/cre/guides/workflow/using-triggers/http-trigger) reference. + + + +## Generating the random number + +In VRF the number arrives in `fulfillRandomWords()`. In CRE you generate it inside the handler with `runtime.Rand()`, which returns a standard Go `*rand.Rand` seeded by the CRE platform so every node produces the same sequence. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-go) for the full reference. + +```go +rnd, err := runtime.Rand() +if err != nil { + return nil, fmt.Errorf("failed to get random generator: %w", err) +} + +randomInt := rnd.Intn(100) // random int in [0, 100) + +// random *big.Int for a Solidity uint256 range [0, max) +max := new(big.Int) +max.SetString("1000000000000000000", 10) // 1 ETH in wei +randomAmount := new(big.Int).Rand(rnd, max) +``` + + + +## Onchain writes + +This is the biggest structural change. In VRF the Coordinator calls your `fulfillRandomWords()` callback. In CRE the workflow ABI-encodes a payload, produces a signed report with `runtime.Report()`, and submits it with `evmClient.WriteReport()` to a consumer that implements `IReceiver` (typically by extending `ReceiverTemplate`), whose handler is `_onReport(bytes metadata, bytes payload)`. + +```go +func onLogTrigger(config *Config, runtime cre.Runtime, log *evm.Log) (*Result, error) { + // Decode the requestId from the RandomnessRequested event + requestId := decodeRequestId(log) + + // Consensus-safe randomness (DON-seeded) + rnd, err := runtime.Rand() + if err != nil { + return nil, fmt.Errorf("failed to get random generator: %w", err) + } + randomWord := new(big.Int).Rand(rnd, maxUint256()) + + // ABI-encode (requestId, randomWord), sign, and write onchain + payload := encodeReport(requestId, randomWord) + report, err := runtime.Report(payload).Await() + if err != nil { + return nil, err + } + + evmClient := evm.Client(CHAIN_SELECTOR) + _, err = evmClient.WriteReport(&evm.WriteReportRequest{ + Receiver: RECEIVER_ADDRESS, + Report: report, + GasLimit: 300000, + }).Await() + return &Result{RequestId: requestId.String()}, err +} +``` + +Receiver contract (replaces the `VRFConsumerBaseV2Plus` consumer): + +```solidity +import {ReceiverTemplate} from "@chainlink/cre-contracts/ReceiverTemplate.sol"; + +contract RandomnessConsumer is ReceiverTemplate { + uint256 private nextRequestId; + mapping(uint256 => bool) public pendingRequests; + mapping(uint256 => uint256) public fulfilledWords; + + event RandomnessRequested(uint256 indexed requestId, address requester); + + constructor(address forwarder) ReceiverTemplate(forwarder) {} + + // Emit event -> CRE workflow fires + function requestRandomness() external returns (uint256 requestId) { + requestId = ++nextRequestId; + pendingRequests[requestId] = true; + emit RandomnessRequested(requestId, msg.sender); + } + + // DON writes back via the Keystone Forwarder + function _onReport(bytes calldata /* metadata */, bytes calldata payload) internal override { + (uint256 reqId, uint256 word) = abi.decode(payload, (uint256, uint256)); + require(pendingRequests[reqId], "unknown request"); + delete pendingRequests[reqId]; // mark fulfilled first + fulfilledWords[reqId] = word; + } +} +``` + +Chain selectors and forwarder addresses are network-specific; see the [Forwarder Directory](/cre/guides/workflow/using-evm-client/forwarder-directory-go) and [Supported Networks](/cre/supported-networks-go). Use `MockKeystoneForwarder` for local simulation. See [Building Consumer Contracts](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts) for how to permission your `IReceiver`. + +## Security patterns that carry over + +These are standard defenses whenever an onchain outcome depends on a value that is only known after a request is made. They apply to CRE randomness exactly as they did to VRF. + +**Do:** + +- **Commit before reveal.** Lock in every user choice (bet side, purchase, entry) in one transaction before the random result is known; reveal only after it arrives. +- **Separate request from consumption.** The random value must come from a future state that is unknowable at request time. Never request and consume randomness in the same transaction. +- **Track request IDs.** Map each request ID to its pending state and verify in `_onReport` that the ID is known and still pending before acting. +- **Mark fulfilled before downstream logic.** Set the fulfilled flag atomically before invoking anything else, closing re-entrancy and replay windows. +- **One value per independent outcome.** Deriving several outcomes from one number (`word % 6` and `word % 10`) correlates them. Generate a separate value per independent outcome. +- **Avoid modulo bias.** Use rejection sampling or big-integer arithmetic when mapping a large number into a small range. + +**Avoid:** + +- **Block variables as entropy** (`block.timestamp`, `blockhash`, `block.prevrandao`) — influenceable by validators. +- **Caller-controlled seeds for high-value draws** — see the trigger caution above. +- **Revert-and-retry ("rerolling")** — if a write can revert and be replayed, the caller gets multiple attempts. Mark fulfilled atomically and guard against acting again after acceptance. + +## Where VRF still fits + +VRF remains available and is the right choice when a contract, regulator, or counterparty must verify a **per-request cryptographic proof** onchain without trusting any operator's execution — for example high-stakes onchain lotteries or casino payouts where each draw needs its own independently checkable proof. CRE secures randomness through DON consensus and the onchain signed-report trail rather than a per-draw proof, which fits the large majority of randomness use cases, especially those already embedded in a broader automated flow. + +## Get started + +To begin, follow the [CRE Getting Started guide](/cre/getting-started/cli-installation) to install the CLI and initialize your first project. + +{/* TODO: switch these links from the `vrf-template` branch to `main` once the randomness template merges. */} + +### Reference template + +The **`randomness`** starter template is a runnable, end-to-end version of this guide — the request/fulfill flow, a `ReceiverTemplate`-based consumer, generated bindings, tests, and a simulation command: + +- [`starter-templates/randomness/randomness-go`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-go) (Go) +- [`starter-templates/randomness/randomness-ts`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-ts) (TypeScript) + +It demonstrates each piece of the migration: + +- An **EVM Log trigger** on a `RandomnessRequested` event — replaces the `requestRandomWords()` entry path. +- A consensus-safe draw with **`runtime.Rand()`** inside the runtime. +- **`runtime.Report()` + `evmClient.WriteReport()`** writing a signed `(requestId, randomWord)` report to a `ReceiverTemplate`-based consumer, whose `_processReport()` records the result — replaces the `fulfillRandomWords()` callback. +- **Request-ID tracking** — the consumer marks each request fulfilled before storing the result, closing reroll/replay windows. + +### Mapping your own integration + +- Use `cre workflow init` (or clone the template above) to scaffold your project. +- Replace `requestRandomWords()` with an appropriate [trigger](#triggers) — usually an EVM Log trigger emitted by your consumer. +- Generate the value with `runtime.Rand()` in the handler; see [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-go). +- Deploy a `ReceiverTemplate`-based consumer that records the signed report in `_processReport()`, replacing the `fulfillRandomWords()` callback. +- Test locally with `cre workflow simulate` before `cre workflow deploy`. + +--- + # Supported Networks Source: https://docs.chain.link/cre/supported-networks-go Last Updated: 2026-07-13 diff --git a/src/content/cre/llms-full-ts.txt b/src/content/cre/llms-full-ts.txt index a7eaaa2ce15..255b792612f 100644 --- a/src/content/cre/llms-full-ts.txt +++ b/src/content/cre/llms-full-ts.txt @@ -23658,6 +23658,257 @@ This section provides a reference for the built-in trigger capabilities of the C --- +# Migrate from Chainlink VRF to Chainlink CRE +Source: https://docs.chain.link/cre/reference/vrf-migration-ts + + + +For new randomness use cases, use the [Chainlink Runtime Environment (CRE)](/cre). Where [Chainlink VRF](/vrf) delivers a random number and nothing else, CRE generates randomness as one step inside a workflow that can also read onchain state, call APIs, run your business logic, and write results to one or more chains — all in a single deployment. + +This guide maps a VRF integration to a CRE workflow: how CRE generates randomness, how that randomness is secured, and how to move the request/fulfill contract flow into a workflow. + +## How VRF and CRE randomness differ + +In **VRF**, your consumer contract inherits `VRFConsumerBaseV2Plus` and calls `requestRandomWords()` on the VRF Coordinator. A Chainlink node generates a random value off-chain together with an ECVRF proof, submits it to the Coordinator, and the Coordinator verifies the proof onchain before calling your `fulfillRandomWords()` callback with the verified random words. Randomness is the entire product; any surrounding logic lives in your contract or an offchain service. + +In **CRE**, the unit of execution is a [**workflow**](/cre/key-terms#workflow) — a TypeScript (or Go) project compiled to WebAssembly and run across a [Decentralized Oracle Network (DON)](/cre/key-terms#decentralized-oracle-network-don). A [**trigger**](/cre/key-terms#trigger) (cron, HTTP, or onchain log) starts the workflow, the handler calls the runtime's randomness function, and the result is written onchain as a DON-signed report to any contract implementing `IReceiver`. Randomness is one [capability](/cre/key-terms#capability) among many the handler can call. + +## How CRE randomness works and how it is secured + +CRE randomness is **consensus-derived**. Every node in the DON runs the same workflow binary and, for a given execution, derives the same per-execution seed. Calling the runtime's random function produces the **same sequence of values on every node**, so the DON can reach [consensus](/cre/concepts/consensus-computing) on a single result. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-ts) for the full behavior. + +What that gives you, and how each part is checkable: + +- **No single node chooses the number.** The value is fixed by the shared seed and reproduced independently by every node. A result is only produced if a Byzantine-fault-tolerant supermajority of the DON agrees on it, so no individual node operator can select or bias the outcome. +- **The result reaches the chain as a DON-signed report.** CRE writes the result through the [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-ts); a consumer implementing `IReceiver` only accepts reports carrying a valid signature from the authorized DON. This produces a tamper-evident, onchain trail of every delivered value. +- **The randomness code is open source and auditable.** The seed derivation and the generator are part of the public SDK and runtime. Anyone can read exactly how a value is produced rather than trusting a description of it. +- **Every result is bound to an identified workflow build.** The `workflowID` — a hash of the compiled workflow binary plus its config — is registered onchain and included in every signed report. This ties each delivered value to a specific, published workflow, so a result cannot be attributed to code that was never registered. See [Verifying Workflows](/cre/guides/operations/verifying-workflows-ts) for how to check a workflow's identity and reproduce its build. +- **Executions are inspectable by your team.** The [CRE dashboard](/cre/guides/operations/monitoring-workflows) shows each execution's handler logs, per-step timings, consensus outcome, and the resulting onchain transaction — for your own operational audit and incident review. + + + +## Chain support + +CRE runs on a broad set of networks. For the canonical, up-to-date list, see [Supported Networks](/cre/supported-networks-ts). + +## Terminology + +| Chainlink VRF | Chainlink CRE | +| :---------------------------------------------- | :------------------------------------------------------------------------------------------------ | +| `VRFConsumerBaseV2Plus` consumer contract | `IReceiver` consumer contract (use `ReceiverTemplate`) | +| `requestRandomWords()` from a consumer | A trigger (EVM Log, Cron, or HTTP) starts the workflow | +| VRF Coordinator | Workflow DON + [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-ts) | +| ECVRF proof verified onchain by the Coordinator | DON consensus + DON-signed report accepted by `IReceiver` | +| `fulfillRandomWords(requestId, randomWords)` | `_onReport(metadata, payload)` on the receiver | +| Off-chain proof generation by a single node | `Math.random()` inside the CRE WASM runtime, agreed by the DON | +| `keyHash` / gas lane | Chain selector + forwarder address per chain | +| LINK/native subscription or direct funding | CRE service model — no per-request subscription to fund | + +## Triggers + +VRF has one entry path: a contract calling `requestRandomWords()`. CRE makes the [trigger](/cre/guides/workflow/using-triggers/overview) first-class, which also determines how predictable the per-execution seed is. + +### Onchain events + +The direct replacement for the VRF request path. Instead of your contract calling a coordinator, the DON watches for an event your contract emits and fires the handler with the decoded event data. + +```ts +const evmClient = new cre.evm.EVMClient(CHAIN_SELECTOR) +const trigger = evmClient.logTrigger({ + addresses: [ + /* base64-encoded contract address */ + ], + topics: [ + /* base64-encoded event signature hash */ + ], +}) +``` + +### Time-based + +Use a [cron trigger](/cre/guides/workflow/using-triggers/cron-trigger-ts) for scheduled draws (periodic raffles, sampling) with no onchain request needed. + +```ts +const trigger = cre.capabilities.cron.trigger({ schedule: "*/5 * * * *" }) // every 5 min +``` + +### HTTP triggers + +For draws requested by an offchain application. The workflow exposes an HTTPS endpoint; callers POST a payload signed with an authorized EVM key. + +```ts +const trigger = cre.capabilities.http.trigger({ + authorizedKeys: [{ type: "EVM", key: "0xYourAuthorizedSigner" }], +}) +``` + + + +## Generating the random number + +In VRF the number arrives in `fulfillRandomWords()`. In CRE you generate it inside the handler with `Math.random()`, which the CRE WASM runtime overrides with a consensus-safe, DON-seeded generator. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-ts) for the full reference. + +```ts +// Math.random() returns a value in [0, 1) +const randomFloat = Math.random() + +// Integer in [0, 100) +const randomInt = Math.floor(Math.random() * 100) + +// bigint for a Solidity uint256 range [0, max) +const max = 1000000000000000000n // 1 ETH in wei +const randomBigInt = BigInt(Math.floor(Number(max) * Math.random())) +``` + + + +## Onchain writes + +This is the biggest structural change. In VRF the Coordinator calls your `fulfillRandomWords()` callback. In CRE the workflow ABI-encodes a payload, produces a signed report with `runtime.report()`, and submits it with `evmClient.writeReport()` to a consumer that implements `IReceiver` (typically by extending `ReceiverTemplate`), whose handler is `_onReport(bytes metadata, bytes payload)`. + +```ts +import { cre } from "@chainlink/cre-sdk" +import { encodeAbiParameters } from "viem" + +export async function main() { + const evmClient = new cre.evm.EVMClient(CHAIN_SELECTOR) + + // EVM Log trigger fires on each RandomnessRequested event + const trigger = evmClient.logTrigger({ + addresses: [ + /* consumer address */ + ], + topics: [ + /* RandomnessRequested signature hash */ + ], + }) + + cre.handler(trigger, async (runtime, event) => { + const { requestId } = decodeEventLog({ + abi: consumerAbi, + eventName: "RandomnessRequested", + data: event.data, + topics: event.topics, + }) + + // Consensus-safe randomness (DON-seeded) + const randomWord = BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) + + const payload = encodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], [requestId, randomWord]) + + const report = await runtime.report(payload).result() + await evmClient.writeReport({ receiver: RECEIVER_ADDRESS, report, gasLimit: "300000" }).result() + }) + + return cre.workflow() +} +``` + +Receiver contract (replaces the `VRFConsumerBaseV2Plus` consumer): + +```solidity +import {ReceiverTemplate} from "@chainlink/cre-contracts/ReceiverTemplate.sol"; + +contract RandomnessConsumer is ReceiverTemplate { + uint256 private nextRequestId; + mapping(uint256 => bool) public pendingRequests; + mapping(uint256 => uint256) public fulfilledWords; + + event RandomnessRequested(uint256 indexed requestId, address requester); + + constructor(address forwarder) ReceiverTemplate(forwarder) {} + + // Emit event -> CRE workflow fires + function requestRandomness() external returns (uint256 requestId) { + requestId = ++nextRequestId; + pendingRequests[requestId] = true; + emit RandomnessRequested(requestId, msg.sender); + } + + // DON writes back via the Keystone Forwarder + function _onReport(bytes calldata /* metadata */, bytes calldata payload) internal override { + (uint256 reqId, uint256 word) = abi.decode(payload, (uint256, uint256)); + require(pendingRequests[reqId], "unknown request"); + delete pendingRequests[reqId]; // mark fulfilled first + fulfilledWords[reqId] = word; + } +} +``` + +Chain selectors and forwarder addresses are network-specific; see the [Forwarder Directory](/cre/guides/workflow/using-evm-client/forwarder-directory-ts) and [Supported Networks](/cre/supported-networks-ts). Use `MockKeystoneForwarder` for local simulation. See [Building Consumer Contracts](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts) for how to permission your `IReceiver`. + +## Security patterns that carry over + +These are standard defenses whenever an onchain outcome depends on a value that is only known after a request is made. They apply to CRE randomness exactly as they did to VRF. + +**Do:** + +- **Commit before reveal.** Lock in every user choice (bet side, purchase, entry) in one transaction before the random result is known; reveal only after it arrives. +- **Separate request from consumption.** The random value must come from a future state that is unknowable at request time. Never request and consume randomness in the same transaction. +- **Track request IDs.** Map each request ID to its pending state and verify in `_onReport` that the ID is known and still pending before acting. +- **Mark fulfilled before downstream logic.** Set the fulfilled flag atomically before invoking anything else, closing re-entrancy and replay windows. +- **One value per independent outcome.** Deriving several outcomes from one number (`word % 6` and `word % 10`) correlates them. Generate a separate value per independent outcome. +- **Avoid modulo bias.** Use rejection sampling or big-integer arithmetic when mapping a large number into a small range. + +**Avoid:** + +- **Block variables as entropy** (`block.timestamp`, `blockhash`, `block.prevrandao`) — influenceable by validators. +- **Caller-controlled seeds for high-value draws** — see the trigger caution above. +- **Revert-and-retry ("rerolling")** — if a write can revert and be replayed, the caller gets multiple attempts. Mark fulfilled atomically and guard against acting again after acceptance. + +## Where VRF still fits + +VRF remains available and is the right choice when a contract, regulator, or counterparty must verify a **per-request cryptographic proof** onchain without trusting any operator's execution — for example high-stakes onchain lotteries or casino payouts where each draw needs its own independently checkable proof. CRE secures randomness through DON consensus and the onchain signed-report trail rather than a per-draw proof, which fits the large majority of randomness use cases, especially those already embedded in a broader automated flow. + +## Get started + +To begin, follow the [CRE Getting Started guide](/cre/getting-started/cli-installation) to install the CLI and initialize your first project. + +{/* TODO: switch these links from the `vrf-template` branch to `main` once the randomness template merges. */} + +### Reference template + +The **`randomness`** starter template is a runnable, end-to-end version of this guide — the request/fulfill flow, a `ReceiverTemplate`-based consumer, generated bindings, tests, and a simulation command: + +- [`starter-templates/randomness/randomness-ts`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-ts) (TypeScript) +- [`starter-templates/randomness/randomness-go`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-go) (Go) + +It demonstrates each piece of the migration: + +- An **EVM Log trigger** on a `RandomnessRequested` event — replaces the `requestRandomWords()` entry path. +- A consensus-safe draw with **`Math.random()`** inside the runtime. +- **`runtime.report()` + `evmClient.writeReport()`** writing a signed `(requestId, randomWord)` report to a `ReceiverTemplate`-based consumer, whose `_processReport()` records the result — replaces the `fulfillRandomWords()` callback. +- **Request-ID tracking** — the consumer marks each request fulfilled before storing the result, closing reroll/replay windows. + +### Mapping your own integration + +- Use `cre workflow init` (or clone the template above) to scaffold your project. +- Replace `requestRandomWords()` with an appropriate [trigger](#triggers) — usually an EVM Log trigger emitted by your consumer. +- Generate the value with `Math.random()` in the handler; see [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-ts). +- Deploy a `ReceiverTemplate`-based consumer that records the signed report in `_processReport()`, replacing the `fulfillRandomWords()` callback. +- Test locally with `cre workflow simulate` before `cre workflow deploy`. + +--- + # Supported Networks Source: https://docs.chain.link/cre/supported-networks-ts Last Updated: 2026-07-13 diff --git a/src/content/cre/reference/vrf-migration-go.mdx b/src/content/cre/reference/vrf-migration-go.mdx new file mode 100644 index 00000000000..64f22cdeb11 --- /dev/null +++ b/src/content/cre/reference/vrf-migration-go.mdx @@ -0,0 +1,242 @@ +--- +section: cre +date: Last Modified +title: "Migrate from Chainlink VRF to Chainlink CRE" +pageId: "vrf-migration" +sdkLang: "go" +metadata: + description: "Migrate onchain randomness from Chainlink VRF to Chainlink CRE. How CRE randomness works and is secured, concept mapping, triggers, generating random values, onchain writes, security patterns, and deployment." + datePublished: "2026-07-23" + lastModified: "2026-07-23" +--- + +import { Aside } from "@components" + + + +For new randomness use cases, use the [Chainlink Runtime Environment (CRE)](/cre). Where [Chainlink VRF](/vrf) delivers a random number and nothing else, CRE generates randomness as one step inside a workflow that can also read onchain state, call APIs, run your business logic, and write results to one or more chains — all in a single deployment. + +This guide maps a VRF integration to a CRE workflow: how CRE generates randomness, how that randomness is secured, and how to move the request/fulfill contract flow into a workflow. + +## How VRF and CRE randomness differ + +In **VRF**, your consumer contract inherits `VRFConsumerBaseV2Plus` and calls `requestRandomWords()` on the VRF Coordinator. A Chainlink node generates a random value off-chain together with an ECVRF proof, submits it to the Coordinator, and the Coordinator verifies the proof onchain before calling your `fulfillRandomWords()` callback with the verified random words. Randomness is the entire product; any surrounding logic lives in your contract or an offchain service. + +In **CRE**, the unit of execution is a [**workflow**](/cre/key-terms#workflow) — a Go (or TypeScript) project compiled to WebAssembly and run across a [Decentralized Oracle Network (DON)](/cre/key-terms#decentralized-oracle-network-don). A [**trigger**](/cre/key-terms#trigger) (cron, HTTP, or onchain log) starts the workflow, the handler calls the runtime's randomness function, and the result is written onchain as a DON-signed report to any contract implementing `IReceiver`. Randomness is one [capability](/cre/key-terms#capability) among many the handler can call. + +## How CRE randomness works and how it is secured + +CRE randomness is **consensus-derived**. Every node in the DON runs the same workflow binary and, for a given execution, derives the same per-execution seed. Calling `runtime.Rand()` produces the **same sequence of values on every node**, so the DON can reach [consensus](/cre/concepts/consensus-computing) on a single result. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-go) for the full behavior. + +What that gives you, and how each part is checkable: + +- **No single node chooses the number.** The value is fixed by the shared seed and reproduced independently by every node. A result is only produced if a Byzantine-fault-tolerant supermajority of the DON agrees on it, so no individual node operator can select or bias the outcome. +- **The result reaches the chain as a DON-signed report.** CRE writes the result through the [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-go); a consumer implementing `IReceiver` only accepts reports carrying a valid signature from the authorized DON. This produces a tamper-evident, onchain trail of every delivered value. +- **The randomness code is open source and auditable.** The seed derivation and the generator are part of the public SDK and runtime. Anyone can read exactly how a value is produced rather than trusting a description of it. +- **Every result is bound to an identified workflow build.** The `workflowID` — a hash of the compiled workflow binary plus its config — is registered onchain and included in every signed report. With Go reproducible builds, anyone with the source can rebuild the binary, recompute that hash, and confirm it matches the workflow registered onchain — binding each delivered value to the exact code that produced it. See [Verifying Workflows](/cre/guides/operations/verifying-workflows-go) for how to check a workflow's identity and reproduce its build. +- **Executions are inspectable by your team.** The [CRE dashboard](/cre/guides/operations/monitoring-workflows) shows each execution's handler logs, per-step timings, consensus outcome, and the resulting onchain transaction — for your own operational audit and incident review. + + + +## Chain support + +CRE runs on a broad set of networks. For the canonical, up-to-date list, see [Supported Networks](/cre/supported-networks-go). + +## Terminology + +| Chainlink VRF | Chainlink CRE | +| :---------------------------------------------- | :------------------------------------------------------------------------------------------------ | +| `VRFConsumerBaseV2Plus` consumer contract | `IReceiver` consumer contract (use `ReceiverTemplate`) | +| `requestRandomWords()` from a consumer | A trigger (EVM Log, Cron, or HTTP) starts the workflow | +| VRF Coordinator | Workflow DON + [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-go) | +| ECVRF proof verified onchain by the Coordinator | DON consensus + DON-signed report accepted by `IReceiver` | +| `fulfillRandomWords(requestId, randomWords)` | `_onReport(metadata, payload)` on the receiver | +| Off-chain proof generation by a single node | `runtime.Rand()` inside the CRE WASM runtime, agreed by the DON | +| `keyHash` / gas lane | Chain selector + forwarder address per chain | +| LINK/native subscription or direct funding | CRE service model — no per-request subscription to fund | + +## Triggers + +VRF has one entry path: a contract calling `requestRandomWords()`. CRE makes the [trigger](/cre/guides/workflow/using-triggers/overview) first-class, which also determines how predictable the per-execution seed is. + +### Onchain events + +The direct replacement for the VRF request path. Instead of your contract calling a coordinator, the DON watches for an event your contract emits and fires the handler with the decoded event data. + +```go +evmClient := evm.Client(CHAIN_SELECTOR) +trigger := evmClient.LogTrigger(&evm.LogTriggerRequest{ + Addresses: [][]byte{/* contract address */}, + Topics: [][]byte{/* event signature hash */}, +}) +``` + +### Time-based + +Use a [cron trigger](/cre/guides/workflow/using-triggers/cron-trigger-go) for scheduled draws (periodic raffles, sampling) with no onchain request needed. + +```go +trigger := cron.Trigger(&cron.Config{Schedule: "*/5 * * * *"}) // every 5 min +``` + +### HTTP triggers + +For draws requested by an offchain application. The workflow exposes an HTTPS endpoint; callers POST a payload signed with an authorized EVM key. See the [HTTP trigger](/cre/guides/workflow/using-triggers/http-trigger) reference. + + + +## Generating the random number + +In VRF the number arrives in `fulfillRandomWords()`. In CRE you generate it inside the handler with `runtime.Rand()`, which returns a standard Go `*rand.Rand` seeded by the CRE platform so every node produces the same sequence. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-go) for the full reference. + +```go +rnd, err := runtime.Rand() +if err != nil { + return nil, fmt.Errorf("failed to get random generator: %w", err) +} + +randomInt := rnd.Intn(100) // random int in [0, 100) + +// random *big.Int for a Solidity uint256 range [0, max) +max := new(big.Int) +max.SetString("1000000000000000000", 10) // 1 ETH in wei +randomAmount := new(big.Int).Rand(rnd, max) +``` + + + +## Onchain writes + +This is the biggest structural change. In VRF the Coordinator calls your `fulfillRandomWords()` callback. In CRE the workflow ABI-encodes a payload, produces a signed report with `runtime.Report()`, and submits it with `evmClient.WriteReport()` to a consumer that implements `IReceiver` (typically by extending `ReceiverTemplate`), whose handler is `_onReport(bytes metadata, bytes payload)`. + +```go +func onLogTrigger(config *Config, runtime cre.Runtime, log *evm.Log) (*Result, error) { + // Decode the requestId from the RandomnessRequested event + requestId := decodeRequestId(log) + + // Consensus-safe randomness (DON-seeded) + rnd, err := runtime.Rand() + if err != nil { + return nil, fmt.Errorf("failed to get random generator: %w", err) + } + randomWord := new(big.Int).Rand(rnd, maxUint256()) + + // ABI-encode (requestId, randomWord), sign, and write onchain + payload := encodeReport(requestId, randomWord) + report, err := runtime.Report(payload).Await() + if err != nil { + return nil, err + } + + evmClient := evm.Client(CHAIN_SELECTOR) + _, err = evmClient.WriteReport(&evm.WriteReportRequest{ + Receiver: RECEIVER_ADDRESS, + Report: report, + GasLimit: 300000, + }).Await() + return &Result{RequestId: requestId.String()}, err +} +``` + +Receiver contract (replaces the `VRFConsumerBaseV2Plus` consumer): + +```solidity +import {ReceiverTemplate} from "@chainlink/cre-contracts/ReceiverTemplate.sol"; + +contract RandomnessConsumer is ReceiverTemplate { + uint256 private nextRequestId; + mapping(uint256 => bool) public pendingRequests; + mapping(uint256 => uint256) public fulfilledWords; + + event RandomnessRequested(uint256 indexed requestId, address requester); + + constructor(address forwarder) ReceiverTemplate(forwarder) {} + + // Emit event -> CRE workflow fires + function requestRandomness() external returns (uint256 requestId) { + requestId = ++nextRequestId; + pendingRequests[requestId] = true; + emit RandomnessRequested(requestId, msg.sender); + } + + // DON writes back via the Keystone Forwarder + function _onReport(bytes calldata /* metadata */, bytes calldata payload) internal override { + (uint256 reqId, uint256 word) = abi.decode(payload, (uint256, uint256)); + require(pendingRequests[reqId], "unknown request"); + delete pendingRequests[reqId]; // mark fulfilled first + fulfilledWords[reqId] = word; + } +} +``` + +Chain selectors and forwarder addresses are network-specific; see the [Forwarder Directory](/cre/guides/workflow/using-evm-client/forwarder-directory-go) and [Supported Networks](/cre/supported-networks-go). Use `MockKeystoneForwarder` for local simulation. See [Building Consumer Contracts](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts) for how to permission your `IReceiver`. + +## Security patterns that carry over + +These are standard defenses whenever an onchain outcome depends on a value that is only known after a request is made. They apply to CRE randomness exactly as they did to VRF. + +**Do:** + +- **Commit before reveal.** Lock in every user choice (bet side, purchase, entry) in one transaction before the random result is known; reveal only after it arrives. +- **Separate request from consumption.** The random value must come from a future state that is unknowable at request time. Never request and consume randomness in the same transaction. +- **Track request IDs.** Map each request ID to its pending state and verify in `_onReport` that the ID is known and still pending before acting. +- **Mark fulfilled before downstream logic.** Set the fulfilled flag atomically before invoking anything else, closing re-entrancy and replay windows. +- **One value per independent outcome.** Deriving several outcomes from one number (`word % 6` and `word % 10`) correlates them. Generate a separate value per independent outcome. +- **Avoid modulo bias.** Use rejection sampling or big-integer arithmetic when mapping a large number into a small range. + +**Avoid:** + +- **Block variables as entropy** (`block.timestamp`, `blockhash`, `block.prevrandao`) — influenceable by validators. +- **Caller-controlled seeds for high-value draws** — see the trigger caution above. +- **Revert-and-retry ("rerolling")** — if a write can revert and be replayed, the caller gets multiple attempts. Mark fulfilled atomically and guard against acting again after acceptance. + +## Where VRF still fits + +VRF remains available and is the right choice when a contract, regulator, or counterparty must verify a **per-request cryptographic proof** onchain without trusting any operator's execution — for example high-stakes onchain lotteries or casino payouts where each draw needs its own independently checkable proof. CRE secures randomness through DON consensus and the onchain signed-report trail rather than a per-draw proof, which fits the large majority of randomness use cases, especially those already embedded in a broader automated flow. + +## Get started + +To begin, follow the [CRE Getting Started guide](/cre/getting-started/cli-installation) to install the CLI and initialize your first project. + +{/* TODO: switch these links from the `vrf-template` branch to `main` once the randomness template merges. */} + +### Reference template + +The **`randomness`** starter template is a runnable, end-to-end version of this guide — the request/fulfill flow, a `ReceiverTemplate`-based consumer, generated bindings, tests, and a simulation command: + +- [`starter-templates/randomness/randomness-go`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-go) (Go) +- [`starter-templates/randomness/randomness-ts`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-ts) (TypeScript) + +It demonstrates each piece of the migration: + +- An **EVM Log trigger** on a `RandomnessRequested` event — replaces the `requestRandomWords()` entry path. +- A consensus-safe draw with **`runtime.Rand()`** inside the runtime. +- **`runtime.Report()` + `evmClient.WriteReport()`** writing a signed `(requestId, randomWord)` report to a `ReceiverTemplate`-based consumer, whose `_processReport()` records the result — replaces the `fulfillRandomWords()` callback. +- **Request-ID tracking** — the consumer marks each request fulfilled before storing the result, closing reroll/replay windows. + +### Mapping your own integration + +- Use `cre workflow init` (or clone the template above) to scaffold your project. +- Replace `requestRandomWords()` with an appropriate [trigger](#triggers) — usually an EVM Log trigger emitted by your consumer. +- Generate the value with `runtime.Rand()` in the handler; see [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-go). +- Deploy a `ReceiverTemplate`-based consumer that records the signed report in `_processReport()`, replacing the `fulfillRandomWords()` callback. +- Test locally with `cre workflow simulate` before `cre workflow deploy`. diff --git a/src/content/cre/reference/vrf-migration-ts.mdx b/src/content/cre/reference/vrf-migration-ts.mdx new file mode 100644 index 00000000000..370515b1fd4 --- /dev/null +++ b/src/content/cre/reference/vrf-migration-ts.mdx @@ -0,0 +1,259 @@ +--- +section: cre +date: Last Modified +title: "Migrate from Chainlink VRF to Chainlink CRE" +pageId: "vrf-migration" +sdkLang: "ts" +metadata: + description: "Migrate onchain randomness from Chainlink VRF to Chainlink CRE. How CRE randomness works and is secured, concept mapping, triggers, generating random values, onchain writes, security patterns, and deployment." + datePublished: "2026-07-23" + lastModified: "2026-07-23" # cache-bust +--- + +import { Aside } from "@components" + + + +For new randomness use cases, use the [Chainlink Runtime Environment (CRE)](/cre). Where [Chainlink VRF](/vrf) delivers a random number and nothing else, CRE generates randomness as one step inside a workflow that can also read onchain state, call APIs, run your business logic, and write results to one or more chains — all in a single deployment. + +This guide maps a VRF integration to a CRE workflow: how CRE generates randomness, how that randomness is secured, and how to move the request/fulfill contract flow into a workflow. + +## How VRF and CRE randomness differ + +In **VRF**, your consumer contract inherits `VRFConsumerBaseV2Plus` and calls `requestRandomWords()` on the VRF Coordinator. A Chainlink node generates a random value off-chain together with an ECVRF proof, submits it to the Coordinator, and the Coordinator verifies the proof onchain before calling your `fulfillRandomWords()` callback with the verified random words. Randomness is the entire product; any surrounding logic lives in your contract or an offchain service. + +In **CRE**, the unit of execution is a [**workflow**](/cre/key-terms#workflow) — a TypeScript (or Go) project compiled to WebAssembly and run across a [Decentralized Oracle Network (DON)](/cre/key-terms#decentralized-oracle-network-don). A [**trigger**](/cre/key-terms#trigger) (cron, HTTP, or onchain log) starts the workflow, the handler calls the runtime's randomness function, and the result is written onchain as a DON-signed report to any contract implementing `IReceiver`. Randomness is one [capability](/cre/key-terms#capability) among many the handler can call. + +## How CRE randomness works and how it is secured + +CRE randomness is **consensus-derived**. Every node in the DON runs the same workflow binary and, for a given execution, derives the same per-execution seed. Calling the runtime's random function produces the **same sequence of values on every node**, so the DON can reach [consensus](/cre/concepts/consensus-computing) on a single result. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-ts) for the full behavior. + +What that gives you, and how each part is checkable: + +- **No single node chooses the number.** The value is fixed by the shared seed and reproduced independently by every node. A result is only produced if a Byzantine-fault-tolerant supermajority of the DON agrees on it, so no individual node operator can select or bias the outcome. +- **The result reaches the chain as a DON-signed report.** CRE writes the result through the [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-ts); a consumer implementing `IReceiver` only accepts reports carrying a valid signature from the authorized DON. This produces a tamper-evident, onchain trail of every delivered value. +- **The randomness code is open source and auditable.** The seed derivation and the generator are part of the public SDK and runtime. Anyone can read exactly how a value is produced rather than trusting a description of it. +- **Every result is bound to an identified workflow build.** The `workflowID` — a hash of the compiled workflow binary plus its config — is registered onchain and included in every signed report. This ties each delivered value to a specific, published workflow, so a result cannot be attributed to code that was never registered. See [Verifying Workflows](/cre/guides/operations/verifying-workflows-ts) for how to check a workflow's identity and reproduce its build. +- **Executions are inspectable by your team.** The [CRE dashboard](/cre/guides/operations/monitoring-workflows) shows each execution's handler logs, per-step timings, consensus outcome, and the resulting onchain transaction — for your own operational audit and incident review. + + + +## Chain support + +CRE runs on a broad set of networks. For the canonical, up-to-date list, see [Supported Networks](/cre/supported-networks-ts). + +## Terminology + +| Chainlink VRF | Chainlink CRE | +| :---------------------------------------------- | :------------------------------------------------------------------------------------------------ | +| `VRFConsumerBaseV2Plus` consumer contract | `IReceiver` consumer contract (use `ReceiverTemplate`) | +| `requestRandomWords()` from a consumer | A trigger (EVM Log, Cron, or HTTP) starts the workflow | +| VRF Coordinator | Workflow DON + [Keystone Forwarder](/cre/guides/workflow/using-evm-client/forwarder-directory-ts) | +| ECVRF proof verified onchain by the Coordinator | DON consensus + DON-signed report accepted by `IReceiver` | +| `fulfillRandomWords(requestId, randomWords)` | `_onReport(metadata, payload)` on the receiver | +| Off-chain proof generation by a single node | `Math.random()` inside the CRE WASM runtime, agreed by the DON | +| `keyHash` / gas lane | Chain selector + forwarder address per chain | +| LINK/native subscription or direct funding | CRE service model — no per-request subscription to fund | + +## Triggers + +VRF has one entry path: a contract calling `requestRandomWords()`. CRE makes the [trigger](/cre/guides/workflow/using-triggers/overview) first-class, which also determines how predictable the per-execution seed is. + +### Onchain events + +The direct replacement for the VRF request path. Instead of your contract calling a coordinator, the DON watches for an event your contract emits and fires the handler with the decoded event data. + +```ts +const evmClient = new cre.evm.EVMClient(CHAIN_SELECTOR) +const trigger = evmClient.logTrigger({ + addresses: [ + /* base64-encoded contract address */ + ], + topics: [ + /* base64-encoded event signature hash */ + ], +}) +``` + +### Time-based + +Use a [cron trigger](/cre/guides/workflow/using-triggers/cron-trigger-ts) for scheduled draws (periodic raffles, sampling) with no onchain request needed. + +```ts +const trigger = cre.capabilities.cron.trigger({ schedule: "*/5 * * * *" }) // every 5 min +``` + +### HTTP triggers + +For draws requested by an offchain application. The workflow exposes an HTTPS endpoint; callers POST a payload signed with an authorized EVM key. + +```ts +const trigger = cre.capabilities.http.trigger({ + authorizedKeys: [{ type: "EVM", key: "0xYourAuthorizedSigner" }], +}) +``` + + + +## Generating the random number + +In VRF the number arrives in `fulfillRandomWords()`. In CRE you generate it inside the handler with `Math.random()`, which the CRE WASM runtime overrides with a consensus-safe, DON-seeded generator. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-ts) for the full reference. + +```ts +// Math.random() returns a value in [0, 1) +const randomFloat = Math.random() + +// Integer in [0, 100) +const randomInt = Math.floor(Math.random() * 100) + +// bigint for a Solidity uint256 range [0, max) +const max = 1000000000000000000n // 1 ETH in wei +const randomBigInt = BigInt(Math.floor(Number(max) * Math.random())) +``` + + + +## Onchain writes + +This is the biggest structural change. In VRF the Coordinator calls your `fulfillRandomWords()` callback. In CRE the workflow ABI-encodes a payload, produces a signed report with `runtime.report()`, and submits it with `evmClient.writeReport()` to a consumer that implements `IReceiver` (typically by extending `ReceiverTemplate`), whose handler is `_onReport(bytes metadata, bytes payload)`. + +```ts +import { cre } from "@chainlink/cre-sdk" +import { encodeAbiParameters } from "viem" + +export async function main() { + const evmClient = new cre.evm.EVMClient(CHAIN_SELECTOR) + + // EVM Log trigger fires on each RandomnessRequested event + const trigger = evmClient.logTrigger({ + addresses: [ + /* consumer address */ + ], + topics: [ + /* RandomnessRequested signature hash */ + ], + }) + + cre.handler(trigger, async (runtime, event) => { + const { requestId } = decodeEventLog({ + abi: consumerAbi, + eventName: "RandomnessRequested", + data: event.data, + topics: event.topics, + }) + + // Consensus-safe randomness (DON-seeded) + const randomWord = BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) + + const payload = encodeAbiParameters([{ type: "uint256" }, { type: "uint256" }], [requestId, randomWord]) + + const report = await runtime.report(payload).result() + await evmClient.writeReport({ receiver: RECEIVER_ADDRESS, report, gasLimit: "300000" }).result() + }) + + return cre.workflow() +} +``` + +Receiver contract (replaces the `VRFConsumerBaseV2Plus` consumer): + +```solidity +import {ReceiverTemplate} from "@chainlink/cre-contracts/ReceiverTemplate.sol"; + +contract RandomnessConsumer is ReceiverTemplate { + uint256 private nextRequestId; + mapping(uint256 => bool) public pendingRequests; + mapping(uint256 => uint256) public fulfilledWords; + + event RandomnessRequested(uint256 indexed requestId, address requester); + + constructor(address forwarder) ReceiverTemplate(forwarder) {} + + // Emit event -> CRE workflow fires + function requestRandomness() external returns (uint256 requestId) { + requestId = ++nextRequestId; + pendingRequests[requestId] = true; + emit RandomnessRequested(requestId, msg.sender); + } + + // DON writes back via the Keystone Forwarder + function _onReport(bytes calldata /* metadata */, bytes calldata payload) internal override { + (uint256 reqId, uint256 word) = abi.decode(payload, (uint256, uint256)); + require(pendingRequests[reqId], "unknown request"); + delete pendingRequests[reqId]; // mark fulfilled first + fulfilledWords[reqId] = word; + } +} +``` + +Chain selectors and forwarder addresses are network-specific; see the [Forwarder Directory](/cre/guides/workflow/using-evm-client/forwarder-directory-ts) and [Supported Networks](/cre/supported-networks-ts). Use `MockKeystoneForwarder` for local simulation. See [Building Consumer Contracts](/cre/guides/workflow/using-evm-client/onchain-write/building-consumer-contracts) for how to permission your `IReceiver`. + +## Security patterns that carry over + +These are standard defenses whenever an onchain outcome depends on a value that is only known after a request is made. They apply to CRE randomness exactly as they did to VRF. + +**Do:** + +- **Commit before reveal.** Lock in every user choice (bet side, purchase, entry) in one transaction before the random result is known; reveal only after it arrives. +- **Separate request from consumption.** The random value must come from a future state that is unknowable at request time. Never request and consume randomness in the same transaction. +- **Track request IDs.** Map each request ID to its pending state and verify in `_onReport` that the ID is known and still pending before acting. +- **Mark fulfilled before downstream logic.** Set the fulfilled flag atomically before invoking anything else, closing re-entrancy and replay windows. +- **One value per independent outcome.** Deriving several outcomes from one number (`word % 6` and `word % 10`) correlates them. Generate a separate value per independent outcome. +- **Avoid modulo bias.** Use rejection sampling or big-integer arithmetic when mapping a large number into a small range. + +**Avoid:** + +- **Block variables as entropy** (`block.timestamp`, `blockhash`, `block.prevrandao`) — influenceable by validators. +- **Caller-controlled seeds for high-value draws** — see the trigger caution above. +- **Revert-and-retry ("rerolling")** — if a write can revert and be replayed, the caller gets multiple attempts. Mark fulfilled atomically and guard against acting again after acceptance. + +## Where VRF still fits + +VRF remains available and is the right choice when a contract, regulator, or counterparty must verify a **per-request cryptographic proof** onchain without trusting any operator's execution — for example high-stakes onchain lotteries or casino payouts where each draw needs its own independently checkable proof. CRE secures randomness through DON consensus and the onchain signed-report trail rather than a per-draw proof, which fits the large majority of randomness use cases, especially those already embedded in a broader automated flow. + +## Get started + +To begin, follow the [CRE Getting Started guide](/cre/getting-started/cli-installation) to install the CLI and initialize your first project. + +{/* TODO: switch these links from the `vrf-template` branch to `main` once the randomness template merges. */} + +### Reference template + +The **`randomness`** starter template is a runnable, end-to-end version of this guide — the request/fulfill flow, a `ReceiverTemplate`-based consumer, generated bindings, tests, and a simulation command: + +- [`starter-templates/randomness/randomness-ts`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-ts) (TypeScript) +- [`starter-templates/randomness/randomness-go`](https://github.com/smartcontractkit/cre-templates/tree/vrf-template/starter-templates/randomness/randomness-go) (Go) + +It demonstrates each piece of the migration: + +- An **EVM Log trigger** on a `RandomnessRequested` event — replaces the `requestRandomWords()` entry path. +- A consensus-safe draw with **`Math.random()`** inside the runtime. +- **`runtime.report()` + `evmClient.writeReport()`** writing a signed `(requestId, randomWord)` report to a `ReceiverTemplate`-based consumer, whose `_processReport()` records the result — replaces the `fulfillRandomWords()` callback. +- **Request-ID tracking** — the consumer marks each request fulfilled before storing the result, closing reroll/replay windows. + +### Mapping your own integration + +- Use `cre workflow init` (or clone the template above) to scaffold your project. +- Replace `requestRandomWords()` with an appropriate [trigger](#triggers) — usually an EVM Log trigger emitted by your consumer. +- Generate the value with `Math.random()` in the handler; see [Using Randomness in Workflows](/cre/guides/workflow/using-randomness-ts). +- Deploy a `ReceiverTemplate`-based consumer that records the signed report in `_processReport()`, replacing the `fulfillRandomWords()` callback. +- Test locally with `cre workflow simulate` before `cre workflow deploy`. diff --git a/src/content/vrf/index.mdx b/src/content/vrf/index.mdx index 5571cf79ff5..081ef2acdb1 100644 --- a/src/content/vrf/index.mdx +++ b/src/content/vrf/index.mdx @@ -13,6 +13,13 @@ metadata: import Vrf2_5Common from "@features/vrf/v2-5/Vrf2_5Common.astro" import { Aside } from "@components" + + **Chainlink VRF (Verifiable Random Function)** is a provably fair and verifiable random number generator (RNG) that enables smart contracts to access random values without compromising security or usability. For each request, Chainlink VRF generates one or more random values and cryptographic proof of how those values were determined. The proof is published and verified onchain before any consuming applications can use it. This process helps ensure that results cannot be tampered with or manipulated by any single entity including oracle operators, smart contract developers, users, miners, or block builders\*. diff --git a/src/content/vrf/llms-full.txt b/src/content/vrf/llms-full.txt index 86ee6c1fc8e..ed917cb87c1 100644 --- a/src/content/vrf/llms-full.txt +++ b/src/content/vrf/llms-full.txt @@ -1,7 +1,12 @@ # Chainlink VRF Source: https://docs.chain.link/vrf - + **Chainlink VRF (Verifiable Random Function)** is a provably fair and verifiable random number generator (RNG) that enables smart contracts to access random values without compromising security or usability. For each request, Chainlink VRF generates one or more random values and cryptographic proof of how those values were determined. The proof is published and verified onchain before any consuming applications can use it. This process helps ensure that results cannot be tampered with or manipulated by any single entity including oracle operators, smart contract developers, users, miners, or block builders\*. @@ -62,6 +67,13 @@ To learn when VRF v2.5 becomes available on more networks, follow us on [Twitter # Getting Started with Chainlink VRF V2.5 Source: https://docs.chain.link/vrf/v2-5/getting-started + +