@chainlink/local

中文文档
0.3.1发布于 2026/10/07MITES Module构建来源已验证近 30 天下载 3558

Chainlink Local Simulator

原始使用文档
文档目录6 节
  1. Chainlink Local
  2. Installation
  3. Usage
  4. Fork mode and CCIP 2.0
  5. Local mode limitations
  6. Learn more

Chainlink Local is an installable dependency. It provides a tool (the Chainlink Local Simulator) that developers import into their Foundry or Hardhat or Remix projects. This tool runs Chainlink CCIP locally which means developers can rapidly explore, prototype and iterate CCIP dApps off-chain in a local environment, and move to testnet only when they're ready to test in a live environment.

The package exposes a set of smart contracts and scripts with which you build, deploy and execute CCIP token transfers and arbitrary messages on a local Remix, Hardhat or Anvil (Foundry) development node. Chainlink Local also supports forked nodes.

User Contracts tested with Chainlink Local can be deployed to test networks without any modifications (assuming network specific contract addresses such as Router contracts and LINK token addresses are passed in via a constructor).

To view more detailed documentation and more examples, visit the Chainlink Local Documentation.

Watch the demo on YouTube

Installation

0.3.x (V3) supports Foundry, Hardhat 3 and Remix IDE. Hardhat 2 users should stay on 0.2.x (npm install @chainlink/[email protected]).

Foundry (git)

forge install smartcontractkit/[email protected]

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

Foundry (soldeer)

forge soldeer install chainlink-local~v0.3.0 https://github.com/smartcontractkit/chainlink-local.git

Hardhat 3 (npm)

npm install @chainlink/[email protected]

Hardhat 3 (Node.js 22) Solidity tests need no remappings: the package ships its own remappings.txt, and its OpenZeppelin and forge-std dependencies are installed with it. Fork tests need evmVersion: "cancun" or later:

// hardhat.config.ts
import { defineConfig } from "hardhat/config";

export default defineConfig({
  solidity: { version: "0.8.24", settings: { evmVersion: "cancun" } },
});
npx hardhat test solidity

For the JavaScript/TypeScript fork helpers (@chainlink/local/scripts/CCIPLocalSimulatorFork.js), also install and register the ethers plugin, and add forked networks:

npm install --save-dev @nomicfoundation/hardhat-ethers ethers
import { configVariable, defineConfig } from "hardhat/config";
import hardhatEthers from "@nomicfoundation/hardhat-ethers";

export default defineConfig({
  plugins: [hardhatEthers],
  solidity: { version: "0.8.24", settings: { evmVersion: "cancun" } },
  networks: {
    sepoliaFork: {
      type: "edr-simulated",
      chainType: "generic",
      chainId: 11155111,
      forking: { url: configVariable("ETHEREUM_SEPOLIA_RPC_URL") },
    },
  },
});

Foundry (npm)

Installing from npm into a Foundry project works too; list the package's dependencies in your remappings.txt (Foundry does not load the remappings of packages under node_modules):

@chainlink/local/=node_modules/@chainlink/local/
@chainlink/contracts-ccip/=node_modules/@chainlink/contracts-ccip/
@chainlink/contracts/=node_modules/@chainlink/contracts/
@openzeppelin/[email protected]/=node_modules/@openzeppelin/contracts-4.8.3/
@openzeppelin/[email protected]/=node_modules/@openzeppelin/contracts-5.3.0/
forge-std/=node_modules/forge-std/src/

Remix IDE (local mode)

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

Remix resolves the @chainlink/contracts and @chainlink/contracts-ccip imports to their latest npm versions.

Once you have installed CCIP Local, you are now ready to start using it with your project.

Usage

Import CCIPLocalSimulator.sol inside your tests or scripts, for example (this is test/smoke/ccip/ReadmeUsageExample.t.sol):

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

import {Test} from "forge-std/Test.sol";
import {
    CCIPLocalSimulator,
    IRouterClient,
    WETH9,
    LinkToken,
    BurnMintERC677Helper
} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
import {ExtraArgsCodec} from "@chainlink/contracts-ccip/contracts/libraries/ExtraArgsCodec.sol";
import {FinalityCodec} from "@chainlink/contracts-ccip/contracts/libraries/FinalityCodec.sol";

contract ReadmeUsageExampleTest is Test {
    CCIPLocalSimulator public ccipLocalSimulator;
    uint64 public chainSelector;
    IRouterClient public sourceRouter;
    LinkToken public linkToken;
    BurnMintERC677Helper public ccipBnM;

    function setUp() public {
        ccipLocalSimulator = new CCIPLocalSimulator();

        WETH9 wrappedNative;
        IRouterClient destinationRouter;
        BurnMintERC677Helper ccipLnM;
        (chainSelector, sourceRouter, destinationRouter, wrappedNative, linkToken, ccipBnM, ccipLnM) =
            ccipLocalSimulator.configuration();
    }

    function test_sendTokens() public {
        address alice = makeAddr("alice");
        address bob = makeAddr("bob");
        ccipLocalSimulator.requestLinkFromFaucet(alice, 5 ether);
        ccipBnM.drip(alice);

        Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1);
        tokenAmounts[0] = Client.EVMTokenAmount({token: address(ccipBnM), amount: 1 ether});

        // Fast Transfers example with 5 block confirmations
        uint32 gasLimit = 0;
        uint16 blockConfirmations = 5;
        bytes4 finalityConfig = FinalityCodec._encodeBlockDepth(blockConfirmations);
        bytes memory extraArgsBytes = ExtraArgsCodec._getBasicEncodedExtraArgsV3(gasLimit, finalityConfig);

        Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
            receiver: abi.encode(bob),
            data: "",
            tokenAmounts: tokenAmounts,
            extraArgs: extraArgsBytes,
            feeToken: address(linkToken)
        });

        vm.startPrank(alice);
        uint256 fee = sourceRouter.getFee(chainSelector, message);
        linkToken.approve(address(sourceRouter), fee);
        ccipBnM.approve(address(sourceRouter), 1 ether);
        sourceRouter.ccipSend(chainSelector, message);
        vm.stopPrank();

        assertEq(ccipBnM.balanceOf(bob), 1 ether);
    }
}

Fork mode and CCIP 2.0

CCIPLocalSimulatorFork routes messages on forked networks for every CCIP era: pre-1.6 (EVM2EVMOnRamp), 1.6 and CCIP 2.0 (CCV-based lanes, which live testnet lanes run today). Requirements for fork tests:

  • Compile fork tests with evm_version = "cancun" or later (deployed CCIP 2.0 contracts use Cancun opcodes; paris fails with EvmError: NotActivated). Use a fork-only profile so your deployed bytecode is not affected: [profile.fork] with evm_version = "cancun" in foundry.toml, then FOUNDRY_PROFILE=fork forge test.
  • Foundry >= 1.5.1 for fork tests (Hardhat 3 requires Node.js 22).
CCIPLocalSimulatorFork ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));

// Send through the router returned by getNetworkDetails(block.chainid).routerAddress, or through the dedicated
// CCIP 2.0 router where one exists: ccipLocalSimulatorFork.getCCIPV2RouterAddress(block.chainid).
// ...ccipSend(...)

ccipLocalSimulatorFork.switchChainAndRouteMessage(destinationForkId);

Routing is strict by default: switchChainAndRouteMessage reverts with CCIPLocalSimulatorFork__MessageNotRouted(messageId, reason) when a captured message cannot be routed to any of the given forks, and with CCIPLocalSimulatorFork__MessageExecutionFailed(messageId, reason) (the decoded revert data) when it does not execute successfully. Call setStrictRouting(false) to record failures instead and read them with getMessageStatus(messageId). With several destinations, either pass every destination fork at once with the uint256[] forkIds overload, or call switchChainAndRouteMessage once per destination fork: a 1.6 or 2.0 message to a chain that is not in the call stays QUEUED and is routed by the call that includes its destination fork.

Hardhat 3 JavaScript/TypeScript tests can route the same way with scripts/CCIPLocalSimulatorFork.js (requires @nomicfoundation/hardhat-ethers; TypeScript declarations ship next to it):

import { network } from "hardhat";
import { getCCIPMessages, routeMessage } from "@chainlink/local/scripts/CCIPLocalSimulatorFork.js";

const source = await network.connect({ network: "sepoliaFork" });
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);
Environment0.3.x0.2.x
Foundry, Hardhat 3 (Solidity tests, local + fork)✅Foundry only
Hardhat 3 JavaScript/TypeScript helpers✅-
Hardhat 2- (not supported: Hardhat 2 cannot resolve the @openzeppelin/[email protected]/ style imports)✅ pre-1.6 fork routing only
Remix IDE (local mode)✅✅

On CCIP 2.0 lanes the destination OffRamp selects the CCVs and executes the message (V2VerificationMode.OFFRAMP_DERIVED, the default); CCV attestations are simulated. Fast Transfer messages with data are only delivered to receivers that opt in through getCCVsAndFinalityConfig, as in production. See CHANGELOG.md for the 0.3.0 breaking changes, migration guide and known limitations.

Local mode limitations

CCIPLocalSimulator delivers messages synchronously inside ccipSend and applies the CCIP 2.0 OnRamp and OffRamp message rules (finality, one token per message, no zero amounts, no V3 tokenReceiver on EVM lanes, V2 receiver CCV config validation). It does not simulate:

  • token pools (pool finality policies, rate limits and pool-required CCVs are not enforced; tokens go directly to the receiver);
  • CCVs or block confirmations (receiver CCV lists are validated, not verified);
  • the supported-token list (getSupportedTokens is informational);
  • manual execution (NO_EXECUTION_ADDRESS messages are executed immediately);
  • the lane's maxPerMsgGasLimit (only V1/V2 extraArgs gas limits above uint32 revert MessageGasLimitTooHigh).

Use fork mode to test these against the real contracts.

Learn more

To view detailed documentation and more examples, visit the Chainlink Local Documentation.

Disclaimer

Please note, this repo contains community examples only — these are not Chainlink products or services and are not supported or maintained by Chainlink. This code represents an example of using a Chainlink product or service, and is intended for demonstration and educational purposes only. It is provided "AS IS" and "AS AVAILABLE" without warranties of any kind, may not have been audited, and may omit checks or error handling. Each party intending to use this example code does so entirely at their own risk and must perform its own audits, security and code review, key management, and testing before any production deployment and ensure the operation and performance of such code matches expectations. Neither Chainlink Labs nor the Chainlink Foundation deploys, operates, monitors, maintains or endorses any deployment of this code. Note that this is not a Chainlink product, feature or service, and there are no commitments made with respect to the code, including compatibility with future Chainlink releases. You should not rely on this code without first conducting your own technical, engineering, and security review. This code is also outside the scope of any Chainlink bug bounty programs. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for outcomes due to errors in this example or how it is deployed or operated, or liable for any resulting claims or damages. Use of the Chainlink Network is subject to the Chainlink Foundation Terms of Service, which provides important information and disclosures. By using this code, you acknowledge and agree to these terms.

版本与发布记录

@chainlink/local 共发布 33 个版本,首次发布于 2024/06/02;过去 12 个月发布了 10 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.1latest2026/10/071 个月前
0.3.02026/10/061 个月前
0.3.0-beta.0beta2026/09/291 个月前
0.3.0-beta2026/09/281 个月前
0.2.92026/05/205 个月前
0.2.9-beta.02026/05/086 个月前

关于 @chainlink/local 的常见问题

@chainlink/local 是什么?

Chainlink Local Simulator

如何安装 @chainlink/local?

在项目目录运行 npm install @chainlink/local,或使用 pnpm add / yarn add / bun add。

@chainlink/local 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/chainlink__local 是否存在。

@chainlink/local 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@chainlink/local 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@chainlink/local 可以商用吗?

@chainlink/local 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@chainlink/local 的下载量有多少?

根据 npm 官方下载统计,@chainlink/local 在 2026/10/10 – 2026/11/08 这 30 天内被下载 3,558 次,日均约 119 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@chainlink/local 还在维护吗?

最新版本 0.3.1 发布于 2026/10/07(1 个月前);至今共发布 33 个版本,其中过去 12 个月发布了 10 个;首次发布于 2024/06/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。