Skip to content

v0.3.0

Choose a tag to compare

@andrejrakic andrejrakic released this 05 Oct 23:03
· 3 commits to main since this release
5e63b91

chainlink local logo

v0.3.0 Release - 5 October 2026

Chainlink Local V3. This is a breaking release: CCIPLocalSimulatorFork and the Hardhat 3
JavaScript fork helper now support CCIP 2.0 (CCV-based) lanes, which is the protocol version live testnet and mainnet
lanes run today, alongside the pre-1.6 and 1.6 eras.

Support matrix

Environment 0.3.x (V3) 0.2.x
Foundry (Solidity tests, local + fork) Supported Supported
Hardhat 3 (Solidity tests, local + fork) Supported -
Hardhat 3 (JavaScript/TypeScript, scripts/*.js helpers) Supported (ESM, @nomicfoundation/hardhat-ethers) -
Hardhat 2 Not supported: the contracts do not compile (Hardhat 2 cannot resolve the @openzeppelin/contracts@4.8.3/ style imports, HH411) Supported (pre-1.6 fork routing only)
Remix IDE (local mode) Supported Supported

Changelog

Dependencies

Package Version
@chainlink/contracts-ccip 2.0.0
@chainlink/contracts 1.5.0

The OpenZeppelin aliases and forge-std are now runtime dependencies (the shipped sources import them). hardhat,
@nomicfoundation/hardhat-ethers and ethers are optional peerDependencies, needed only for the JavaScript helpers.
engines.node is >=22. forge-std is a git dependency (the npm forge-std package is stale), so npm install needs
access to GitHub.

lib/chainlink-ccip is pinned to tag contracts-ccip-v2.0.0 (c2c125c27f056db2e98d21501922b6eff5750f36) in the
gitlink, .gitmodules (no branch = develop) and foundry.lock, so forge install and forge update resolve it
to the tag.

Breaking changes

  • @chainlink/contracts-ccip 1.6.2 -> 2.0.0. Upstream CCIP 2.0 moved every finality value from uint16 block
    confirmations to a bytes4 FinalityCodec config. This changes the on-chain wire format of GenericExtraArgsV3
    (blockConfirmations -> requestedFinalityConfig, base size 17 -> 19 bytes) and of MessageV1 (finality).
    Receivers implement getCCVsAndFinalityConfig(...) returning bytes4 allowedFinalityConfig instead of
    getCCVsAndMinBlockDepth(...) returning uint16 minBlockDepth; token pools use setAllowedFinalityConfig(bytes4)
    instead of setMinBlockConfirmations(uint16). See the migration guide below.

  • Default V2VerificationMode is now OFFRAMP_DERIVED (was HYBRID). CCIP 2.0 messages are executed through the
    destination OffRamp's permissionless execute with the CCVs it selects (getCCVsForMessage), without decoding the
    message locally. STRICT, HYBRID and SYNTHETIC_ONLY remain available through setV2VerificationMode.

  • Fork routing is strict by default. switchChainAndRouteMessage (and executePendingV2Message) now revert when a
    captured CCIP message cannot be routed to any of the given forks
    (CCIPLocalSimulatorFork__MessageNotRouted(messageId, reason)) or does not execute successfully
    (CCIPLocalSimulatorFork__MessageExecutionFailed(messageId, reason), with the decoded revert data, e.g.
    InvalidRequestedFinality(...) or the receiver's revert). 0.2.6 to 0.2.9 logged execution failures and carried on, and
    dropped unroutable messages without any output; 0.2.5 and earlier reverted. Call setStrictRouting(false) to log and
    record failures instead (see getMessageStatus). A 1.6 or 2.0 message to a chain that is not in forkIds is not a
    failure: it stays QUEUED and is routed by a later switchChainAndRouteMessage call that includes its destination
    fork (0.2.x dropped it). Pre-1.6 logs do not name their destination, so route them in a call that includes it.

  • Fast Transfer delivery follows production rules. OffRamp 2.0 only delivers a Fast Transfer message with data (or a
    non-zero gas limit) to a receiver that opts in through getCCVsAndFinalityConfig. A plain CCIPReceiver only accepts
    finalized messages, so the OffRamp records the execution as FAILURE: routing reverts with
    InvalidRequestedFinality in strict mode, or records it as FAILED.

  • Fork mode needs evm_version = "cancun" or later (not paris/shanghai): deployed CCIP 2.0 contracts use Cancun
    opcodes, and paris fails with EvmError: NotActivated. Set it only for fork tests (see the migration guide), so
    your deployed bytecode is not affected.

  • Foundry >= 1.5.1 is required for fork tests. forge 1.1.0 panics (revm journaled_state.rs:402) when an OffRamp
    2.0 execution records a FAILURE.

  • Local mode applies the CCIP 2.0 OnRamp and OffRamp rules. CCIPLocalSimulator now uses CCIPLocalRouter
    (same ABI as the upstream MockCCIPRouter, which it replaces). ccipSend now reverts where production rejects the
    message, with the production error:

    • InvalidRequestedFinality(requested, allowed): a Fast Transfer message with data or a non-zero gas limit to a
      receiver that does not allow that finality. RequestedFinalityCanOnlyHaveOneMode: a malformed finality config.
    • For every message that is not a token-only transfer to an IAny2EVMMessageReceiverV2 receiver (finalized messages
      included), getCCVsAndFinalityConfig is called and validated: its revert, DuplicateCCVNotAllowed or
      InvalidOptionalThreshold fails the send.
    • TokenReceiverNotAllowed(destChainSelector): a non-empty GenericExtraArgsV3.tokenReceiver (rejected on EVM lanes).
    • CanOnlySendOneTokenPerMessage (more than one token) and CannotSendZeroTokens (a zero amount).
    • MessageGasLimitTooHigh: a GenericExtraArgsV2 / EVMExtraArgsV1 gas limit above type(uint32).max (the upstream
      mock truncated it to 32 bits; production rejects it against the lane's uint32 maxPerMsgGasLimit).

    ExtraArgs shorter than 4 bytes are now treated as empty (default gas limit), as in production, instead of reverting
    InvalidExtraArgsTag. Local messageIds are now unique per send (they include a nonce, the sender and the
    destination), so IDs differ from earlier versions and identical sends no longer collide.

  • src/vendor/** was removed (70 files: vendored OpenZeppelin 4.7.3 / 4.8.3 / 5.0.2, MockRouter,
    BurnMintERC677 and shared Chainlink contracts). Sources import @openzeppelin/contracts@4.8.3/...,
    @openzeppelin/contracts@5.3.0/... and @chainlink/contracts(-ccip) instead; CCIPLocalSimulator's AccessControl
    support now uses OpenZeppelin 5.3.0 (was 5.0.2). Imports of @chainlink/local/src/vendor/... must move to those
    packages.

  • Register keeps its built-in network details in code. getNetworkDetails, setNetworkDetails and
    NetworkDetails are unchanged. The details are returned by a generated pure function instead of being written to
    storage in the constructor, and CCIPLocalSimulatorFork places its Register with vm.etch. Code that read
    Register storage slots directly breaks.

  • Register data is regenerated from the CCIP directory API (https://docs.chain.link/api/ccip/v1, Sep 2026):
    134 chains (was 148). Router addresses are unchanged for every chain in both versions.

    • wrappedNativeAddress changes on 17 chains. The generator used the first non-LINK fee token, which was GHO on
      e.g. Ethereum, Ethereum Sepolia, Arbitrum One, Arbitrum Sepolia and Base, and a bridged WETH on Monad Testnet and
      Plasma Testnet; it now picks the wrapped native token (e.g. WETH, WAVAX, WMON). It is address(0) on Stable and
      Botanix, where the API lists no unambiguous wrapped native token.
    • Removed (no longer in the API, or without fee tokens): Blast, Blast Sepolia, Polygon zkEVM, Polygon zkEVM Cardona,
      Scroll Sepolia, Mint, Mint Sepolia, Mind Network, Mind Network Testnet, Tac, Everclear, Memento, Memento Testnet,
      Tempo (42429), Corn, Corn Testnet, Janction Testnet, B² Testnet, Kroma Sepolia, Fraxtal Testnet, Botanix Testnet,
      MegaETH Testnet (6342), Superseed Sepolia, Berachain Bartio, Katana Tatara, Taiko Hekla, Bitlayer Testnet and
      Treasure Topaz. getNetworkDetails returns an all-zero struct for them; use setNetworkDetails.
    • Added: Avalanche, Kaia, Neo X, Robinhood Chain, Robinhood Chain Testnet, HyperEVM Testnet, Tempo Mainnet, Tempo
      Testnet Moderato, MegaETH Testnet (6343), Mova Mainnet, Mova Testnet, 0G Galileo Testnet, Creditcoin Mainnet and
      Ronin Sepolia.
    • Changed on existing chains: 8 LINK addresses, 8 CCIP-BnM addresses and 3 registryModuleOwnerCustomAddresses, as
      listed by the API.
  • Hardhat JavaScript helpers target Hardhat 3 (ES modules; they did not load in Hardhat 3 before). Every function
    takes the NetworkConnection from network.connect() and needs @nomicfoundation/hardhat-ethers:

    • scripts/CCIPLocalSimulatorFork.js: getEvm2EvmMessage(receipt) -> getCCIPMessages(connection, receipt)
      (all eras); routeMessage(routerAddress, message) -> routeMessage(connection, routerAddresses, sent, options);
      requestLinkFromTheFaucet(linkAddress, to, amount) -> requestLinkFromTheFaucet(connection, linkAddress, to, amount).
    • scripts/data-streams/: new MockReportGenerator(initialPrice) -> new MockReportGenerator(connection, initialPrice);
      requestLinkFromFaucet / requestNativeFromFaucet take connection first.
    • Hardhat 2 JavaScript users: stay on 0.2.x (see the support matrix).
  • Removed IRouterFork.OffRamp; IRouterFork.getOffRamps() returns CCIPForkAdapterTypes.RouterOffRamp[]
    (ABI-identical: (uint64 sourceChainSelector, address offRamp)).

  • Test helpers (not shipped in the npm package, but commonly copied): EncodeExtraArgsOffchain.encodeV3 takes
    bytes4 requestedFinalityConfig, encodeV3Basic(uint32, bytes4) takes a finality config and the new
    encodeV3BasicBlockDepth(uint32, uint16) takes a block depth; BasicMessageReceiverWithCCVs.setMinBlockDepth is
    replaced by setAllowedFinalityConfig(uint64, bytes4).

Added

  • CCIP 2.0 routing in CCIPLocalSimulatorFork (adapter architecture in src/ccip/adapters/, era detection from the
    emitted event), including the NO_EXECUTION_ADDRESS queue
    (V2ExecutionMode, getPendingV2MessageIds, executePendingV2Message).
  • V2VerificationMode.OFFRAMP_DERIVED: routes CCIP 2.0 messages
    without the local MessageV1 codec, so fork tests keep working when the on-chain wire format moves ahead of the
    pinned dependency. CCVs that are owner-configurable verifier resolvers are pointed at a synthetic fork verifier, so
    live lanes work without mocking; getOffRampForLane, setLaneDefaultCCVs and the CCVNoOpVerifier test double
    (src/test/ccip/CCVNoOpVerifier.sol) remain available to mock a lane's default CCV instead.
  • Hardhat 3 JavaScript fork routing for 1.6 and CCIP 2.0 in scripts/CCIPLocalSimulatorFork.js (pre-1.6 kept,
    including EVM2EVMOffRamp 1.2 and 1.5), with the same typeAndVersion-based OffRamp lookup and CCIP 2.0 execution as
    the Solidity simulator; CCIP_V2_ROUTERS; execution failures are thrown with a decoded reason. Tests:
    npm run hardhat-test-js (fork) and npm run js-unit-test (offline: log decoding for every era from pinned ABIs,
    OffRamp lookup and execution against mocked OffRamps).
  • TypeScript declarations for the CCIP JavaScript helper (scripts/CCIPLocalSimulatorFork.d.ts, shipped in the npm
    package).
  • CI workflow (.github/workflows/test.yml): Foundry build, fmt --check and non-fork tests, Hardhat Solidity and
    JavaScript unit tests, and an npm-tarball consumer smoke test (.github/scripts/consumer-smoke.sh) for Hardhat 3 and
    Foundry (npm). Fork suites run on manual dispatch with RPC secrets.
  • src/ccip/CCIPLocalRouter.sol, the local-mode router with the CCIP 2.0 OnRamp and OffRamp checks.
  • CCIPLocalSimulatorFork.getMessageStatus(messageId) returns NOT_FOUND, QUEUED, FAILED or SUCCESS and the
    failure reason; setStrictRouting(bool) / getStrictRouting().
  • Mainnet CCIP 2.0 fork test (Ethereum -> Arbitrum One; needs ETHEREUM_MAINNET_RPC_URL / ARBITRUM_MAINNET_RPC_URL).
  • CCIP 2.0 routers: getCCIPV2RouterAddress(chainId) / setCCIPV2RouterAddress(chainId, router). Some chains run a
    dedicated CCIP 2.0 router next to the router getNetworkDetails returns (Ethereum Sepolia
    0x784d49a71BB4C48eB7dA4cD7e6Ecb424f9b5EAB1, Avalanche Fuji 0x7C9B8B4e8024e5Ee8A630F6FCe9015e470dA5763, seeded by
    default). switchChainAndRouteMessage routes messages sent through either router. Register / getNetworkDetails
    are unchanged (routerAddress is still the router from the CCIP docs API).

Removed

  • abi/*.json (EVM2EVMOnRamp, EVM2EVMOffRamp, OnRamp, Router, LinkToken) are no longer shipped in the npm
    package; the JavaScript helpers use inline human-readable ABIs. Import ABIs from @chainlink/contracts-ccip /
    @chainlink/contracts artifacts instead.
  • Hardhat 2 example scripts (scripts/examples/) and Hardhat 2 .spec.ts tests, which could not run on Hardhat 3.

Fixed

  • switchChainAndRouteMessage no longer reverts when a router lists OffRamps of several versions for the same source
    chain (e.g. 1.2, 1.5, 1.6 and 2.0 during lane migrations). OffRamp 1.6 and 2.0 share getSourceChainConfig(uint64)
    and getStaticConfig() selectors with other versions but return different structs, and a mismatched return payload
    failed to decode in the caller, which try/catch does not catch. OffRamps are now matched by typeAndVersion
    (OffRamp 2.* against onRamps[], OffRamp 1.6* against onRamp, EVM2EVMOffRamp 1.* against getStaticConfig),
    every decode is isolated, and an OffRamp of unknown shape is skipped.
  • CCIP 2.0 (OFFRAMP_DERIVED): a message is marked processed only when the OffRamp reports SUCCESS. OffRamp 2.0
    execute does not revert when verification or the receiver fails on a first attempt; it records FAILURE, which was
    previously treated as delivered. Only the OffRamp bound to the emitting OnRamp is executed (previously every OffRamp on
    the router was tried), and RESPECT_NO_EXEC / MANUAL_ONLY queueing now applies in this mode too.
  • CCIP 2.0: an unexpected getCCVsForMessage or verifier-resolver owner() response (including a CCV without code) no
    longer reverts routing.
  • v1.6: Any2EVMRampMessage.sender is abi.encode(address) (32 bytes, as production 1.6 lanes deliver) instead of
    abi.encodePacked (20 bytes); receivers doing abi.decode(message.sender, (address)) reverted (#62 / #65).
  • CCIP 2.0 CCV selection matches the OffRamp quorum. The simulator passed max(required, threshold) CCVs in total,
    so a receiver with required CCVs plus optional CCVs (e.g. required=[A], optional=[B,C], threshold=1) failed with
    OptionalCCVQuorumNotReached where production delivers. It now passes every required CCV plus threshold optional
    ones (optional CCVs that are also required count toward the threshold), in the Solidity simulator, the local-codec
    adapter and the JavaScript helper.
  • 1.6 messages execute only on their destination fork. 1.6 OnRamps serve every destination from one address, so
    with several destination forks a 1.6 message could execute on the first fork whose OffRamp served the source lane
    (and be marked processed there). 1.6 logs are now filtered by destChainSelector like 2.0 logs.
  • CCIPLocalSimulatorFork deploys in ~14.4M gas (was ~36M): new CCIPLocalSimulatorFork() right after
    createSelectFork ran out of gas on forks with a 30M block gas limit.
  • setLaneDefaultCCVs keeps the lane's laneMandatedCCVs (it cleared them, making the fork lane less strict than
    production).
  • Routing hardening: a 32-byte OnRamp or owner word that is not an address is skipped instead of reverting the lookup;
    a 2.0 log whose messageId topic differs from keccak256(encodedMessage) is rejected; a foreign event with a CCIP
    event signature but fewer indexed topics no longer panics routing; the unused OnRamp era probe was removed (routing
    always followed the event's era, and the probe classified "EVM2EVMOnRamp 1.2.0" as 2.0); the 2.0 destination filter reads topics[1] in every
    V2VerificationMode, so a non-default mode no longer decodes MessageV1 before the emitter check.
  • The npm package compiles for Hardhat 3 and npm-based Foundry consumers. It ships a remappings.txt for
    node_modules layouts (remappings-npm.txt, swapped in by the prepack script and restored by postpack; git and
    forge install consumers keep the lib/ remappings), so Hardhat 3 no longer fails with HHE902 on the
    @openzeppelin/contracts@4.8.3/... imports.
  • The JavaScript helpers throw a clear error naming @nomicfoundation/hardhat-ethers when the connection has no
    ethers (was Cannot read properties of undefined (reading 'Interface')), and decode OptionalCCVQuorumNotReached,
    InvalidOptionalThreshold, InvalidVerifierResultsLength, NoStateProgressMade and InvalidEVMAddress.
  • 1.6 and pre-1.6 fork execution (Solidity and JavaScript) passes zero tokenGasOverrides, so each token keeps the destGasAmount stamped by the
    source OnRamp, as when a DON executes the message. The message gas limit was passed as every token's release/mint
    gas budget instead.
  • CCIP 2.0 (OFFRAMP_DERIVED and the JavaScript helper): the OffRamp stamped in the message
    (MessageV1.offRampAddress) is used when it serves the lane. OffRamp 2.0 rejects a message stamped with another OffRamp (InvalidOffRamp), and the router lookup returned
    the newest OffRamp of the lane, which differs while an OffRamp is being replaced.
  • The synthetic CCV setup is read from the resolver on every route instead of being cached, and the no-op verifier is
    redeployed when it has no code: after vm.rollFork or a snapshot revert on the destination fork, 2.0 messages failed
    with the cached (now missing) configuration.
  • The single-fork switchChainAndRouteMessage(forkId) always returns with forkId selected, as documented (it stayed on
    the source fork when nothing was routed).
  • CCIPLocalRouter declares pragma solidity ^0.8.20, the floor its OpenZeppelin 5.3.0 imports already required.
  • The JavaScript helper checks that a 1.6 message's destination is the connected chain before executing it (1.6 OnRamps
    serve several destinations, and executeSingleMessage does not check it), and every execution transaction's revert
    is thrown with the decoded reason (the original error is kept as cause); it also decodes TokenHandlingError,
    ReleaseOrMintBalanceMismatch, InvalidMessageDestChainSelector and CursedByRMN.
  • The npm package lists the CCIP JavaScript helper with its actual file name (scripts/CCIPLocalSimulatorFork.js); the
    lowercase entry did not match on case-sensitive file systems, so published packages could omit the helper.
  • Register built-in network details are split into generated data shards (RegisterData.sol) so every generated
    contract stays under the EIP-170 runtime limit. Lookup behaviour is unchanged; the simulator places the shards
    with vm.etch alongside Register (a bare new Register() now serves setNetworkDetails overrides only).

Migration guide (0.2.x -> 0.3.0)

  1. Bump @chainlink/contracts-ccip to 2.0.0 (Foundry: lib/chainlink-ccip at contracts-ccip-v2.0.0) and
    @chainlink/local to 0.3.0-beta (published under the beta tag: npm install @chainlink/local@0.3.0-beta,
    forge install smartcontractkit/chainlink-local@v0.3.0-beta). Use Node.js 22 for Hardhat 3. Hardhat 3 Solidity
    tests need no remappings; Foundry projects installing from npm list the package's dependencies in remappings.txt
    (see the README).
  2. Fork tests: compile them with cancun or later, without changing the EVM version your contracts are deployed with,
    and use Foundry >= 1.5.1 (forge 1.1.0 panics on a failed CCIP 2.0 execution). For example, add a fork profile to
    foundry.toml and run fork tests with FOUNDRY_PROFILE=fork forge test:
    [profile.fork]
    evm_version = "cancun"
    or pass --evm-version cancun to forge test. In Hardhat 3, set evmVersion: "cancun" in the Solidity settings of
    the build profile you run fork tests with.
  3. Extra args V3: replace block confirmations with a FinalityCodec config.
    // before: ExtraArgsCodec._getBasicEncodedExtraArgsV3(gasLimit, uint16(blockConfirmations))
    ExtraArgsCodec._getBasicEncodedExtraArgsV3(gasLimit, FinalityCodec.WAIT_FOR_FINALITY_FLAG); // finality
    ExtraArgsCodec._getBasicEncodedExtraArgsV3BlockDepth(gasLimit, 5); // Fast Transfer, 5 blocks
    ExtraArgsCodec._getBasicEncodedExtraArgsV3FastConfirmationRule(gasLimit); // wait for the `safe` tag
    GenericExtraArgsV2 / EVMExtraArgsV1 are unaffected.
  4. Receivers implementing IAny2EVMMessageReceiverV2: rename getCCVsAndMinBlockDepth to getCCVsAndFinalityConfig
    and return bytes4 (FinalityCodec.WAIT_FOR_FINALITY_FLAG for finality only, FinalityCodec._encodeBlockDepth(n)
    to accept Fast Transfers of at least n blocks). A receiver must opt in this way to receive Fast Transfer messages with data.
  5. Token pools: setMinBlockConfirmations(n) -> setAllowedFinalityConfig(FinalityCodec._encodeBlockDepth(n)). Read a
    live pool's policy with getAllowedFinalityConfig(); requested Fast Transfer block depth must be >= the pool's allowed depth.
  6. Fork tests on CCIP 2.0 lanes need no extra setup in the default mode. If you relied on the previous default, call
    setV2VerificationMode(V2VerificationMode.HYBRID). If you referenced IRouterFork.OffRamp, use
    CCIPForkAdapterTypes.RouterOffRamp.
  7. Local mode: Fast Transfer messages with data now need an opted-in receiver (step 4), as on a fork and in production. Tests that
    sent Fast Transfer data to a plain CCIPReceiver must switch the receiver or request finality.
  8. Hardhat JavaScript/TypeScript tests: move to Hardhat 3 and @nomicfoundation/hardhat-ethers, and pass the network
    connection to the helpers:
    import { network } from "hardhat";
    import { getCCIPMessages, routeMessage } from "@chainlink/local/scripts/CCIPLocalSimulatorFork.js";
    
    const source = await network.connect({ network: "sepoliaFork" }); // edr-simulated networks with `forking`
    const destination = await network.connect({ network: "arbitrumSepoliaFork" });
    const receipt = await (await sourceRouter.ccipSend(destChainSelector, message, { value: fee })).wait();
    const [sent] = getCCIPMessages(source, receipt);
    await routeMessage(destination, destinationRouterAddress, sent); // throws with the reason if execution fails
    Pass an array of routers (e.g. [routerAddress, CCIP_V2_ROUTERS[chainId]]) to route through dedicated CCIP 2.0
    routers, and { forceExecution: true } to execute a NO_EXECUTION_ADDRESS message.
  9. Strict routing: tests that tolerated failed or unroutable messages now revert. Assert the failure with
    vm.expectRevert(abi.encodeWithSelector(CCIPLocalSimulatorFork.CCIPLocalSimulatorFork__MessageExecutionFailed.selector, messageId, expectedReason)),
    or call setStrictRouting(false) and check getMessageStatus(messageId).
  10. Local mode: sends with more than one token, a zero token amount, a non-empty V3 tokenReceiver or a V1/V2 gas limit
    above type(uint32).max now revert, as in production; V2 receivers' getCCVsAndFinalityConfig must not revert and must return a valid config; do not rely on
    local messageId values being equal for identical sends.
  11. Register: if you used wrappedNativeAddress on the chains listed under Breaking changes, it now points at the
    wrapped native token (it was GHO on e.g. Ethereum Sepolia). For a removed chain, set its details with
    setNetworkDetails.
  12. Replace @chainlink/local/src/vendor/... imports with @openzeppelin/contracts@4.8.3/...,
    @openzeppelin/contracts@5.3.0/... or @chainlink/contracts-ccip/....
  13. To use a dedicated CCIP 2.0 router, send through getCCIPV2RouterAddress(block.chainid) and deploy receivers with
    the destination chain's CCIP 2.0 router (the destination router must be the one whose OffRamp delivers the message).

Testing the release

To test this release install @chainlink-local using the following commands:

Foundry (git)

forge install smartcontractkit/chainlink-local@v0.3.0

and then set remappings to: @chainlink/local/=lib/chainlink-local/ in either remappings.txt or foundry.toml file

Hardhat (npm)

npm install @chainlink/local@v0.3.0

and then create the following contract and compile it:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {CCIPLocalSimulator} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";

Remix IDE

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {CCIPLocalSimulator} from "https://github.com/smartcontractkit/chainlink-local/blob/v0.3.0/src/ccip/CCIPLocalSimulator.sol";

PRs included

New Contributors

Full Changelog: v0.2.9...v0.3.0