DEVREL 205: Chainlink Local V3 - #54
Open
andrejrakic wants to merge 21 commits into
Open
andrejrakic wants to merge 21 commits into
andrejrakic wants to merge 21 commits into
Conversation
* refactor: Migrate to HH3, update dependencies, delete vendor folder * feat: Add ExtraArgsV3 test
…olidity-docgen dependencies and relying on forge docs instead plus some scripts to keep the formatting bacwards compatible
Conflict resolution: - src/ccip/CCIPLocalSimulatorFork.sol: kept the V3 era routing + adapters and ported develop's routing fixes into them: exact on-ramp -> off-ramp matching (_findOffRampForOnRamp via getSourceChainConfig/getStaticConfig), 20/32-byte EVM address decoding, and success-only processed-message marking. V2 OffRamp discovery now derives the source on-ramp from the decoded message. - CCIPForkAdapterV1dot6: sourcePoolAddress is abi-encoded, matching the v1.6 OnRamp wire format (develop's fix); the adapter unit test now decodes it. - Kept develop's new tests: CCIPLocalSimulatorForkRouting.t.sol (7 cases) and EthSepoliaToArbSepoliaMultiOffRampFork.t.sol (import adapted to the V3 layout). Unit: 42/42, smoke: 28/28. - docs/config/publish workflow: develop's versions; package.json/package-lock: V3 packaging kept (type: module, helper_doc scripts); develop's 0.2.9 version bump is superseded by 0.3.0-beta. - scripts/data-streams/*.js: develop's (V3 changes were JSDoc-only). - lib/chainlink-ccip: kept the V3 pin 347f5118b (newer than develop's 0e3e0fc5c). Known follow-up (not in this merge): live lanes now encode MessageV1 finality as bytes4 (chainlink-ccip #1898, 2026-04-08); the pinned dependency still decodes uint16, so live E2E V2 routing fails with InvalidDataLength(MESSAGE_FINAL_OFFSET). Bumping chainlink-ccip requires the finality/blockConfirmations API migration across the V3 branch and should be coordinated with the refresh owner.
This was referenced Sep 18, 2026
Closed
Closed
chore: sync develop into cl-local-v3 (refresh #54)
Adds V2VerificationMode.OFFRAMP_DERIVED: the required CCV list comes from the destination OffRamp (getCCVsForMessage) and the raw encoded message is executed through the permissionless execute entrypoint, so fork routing does not depend on the local MessageV1 codec. Keeps V2 fork tests working when the on-chain wire format moves ahead of the pinned chainlink-ccip (today: finality uint16 -> bytes4, chainlink-ccip #1898). - CCIPLocalSimulatorFork: new mode + _routeV2MessageOffRampDerived / _executeOffRampDerivedV2; destination chain resolved from the event topics with no MessageV1 decode; falls back across candidate OffRamps and marks processed only on success. - Fork-only test helpers ported onto V3: getOffRampForLane, setLaneDefaultCCVs, IOffRampSourceConfigV2Fork, plus the CCVNoOpVerifier test double. - IOffRampExecuteV2 gains the bytes-based execute entrypoint. - New e2e: test/e2e/ccip/CCIPv2Fork.t.sol (Sepolia -> Arb Sepolia message and token transfer over the live 2.0 lane with a no-op default CCV). - Unit 42/42; live e2e 2/2 with public RPCs.
Brings in fdb0cc7 (Correct v1.6 sender encoding, #65) and dd13ac9 (v0.2.10-beta prep). Conflict resolution: - src/ccip/CCIPLocalSimulatorFork.sol: keep V3's adapter layout; drop develop's inline _executePreV1dot6/_executePostV1dot6 (V3 moved that logic into src/ccip/adapters/). - Port #65 into CCIPForkAdapterV1dot6._toAny2EVMMessage: sender is now abi.encode(address) (32-byte word) instead of abi.encodePacked. - test/unit/ccip/CCIPLocalSimulatorForkRouting.t.sol: port #65's regression test onto the adapter mirror types (pinned chainlink-ccip no longer ships the 1.6 Internal ramp structs). Verified it fails with 20 != 32 before the port. - package.json / package-lock.json: keep V3's 0.3.0-beta version. - CHANGELOG.md: keep both the Unreleased and 0.2.10-beta sections.
- Pin chainlink-ccip to contracts-ccip-v2.0.0 and @chainlink/contracts-ccip to 2.0.0; migrate finality to bytes4 FinalityCodec (extraArgs V3, receivers, pools, test helpers). - CCIPLocalSimulatorFork: make OFFRAMP_DERIVED (magiodev-cll#3) the default V2 mode; mark processed only on SUCCESS, execute only the lane OffRamp, honour the NO_EXEC queue, synthetic results for resolver CCVs. - Fix the OffRamp lookup reverting on mixed-era router lists: dispatch on typeAndVersion, isolate every decode, skip unknown shapes. - Route dedicated CCIP 2.0 routers (getCCIPV2RouterAddress / setCCIPV2RouterAddress; Sepolia and Fuji seeded). - Local mode: CCIPLocalRouter enforces CCIP 2.0 receiver finality for Fast Transfers. - Hardhat 3 JS helpers: CCIP (all eras) and Data Streams rebuilt as ESM taking a network connection; fix npm files casing. - Remove dead code: abi/*.json (no longer shipped), Hardhat 2 examples and .spec.ts tests, duplicate IRouterFork.OffRamp and _decodeEVMAddress. - evm_version = cancun; version 0.3.0-beta.0. - Tests: unit tests for lookup, V2 routing and local finality; fork suites for Sepolia<->Fuji 2.0, mainnet 2.0, pinned-block 1.6; Hardhat 3 JS tests. - Docs: CHANGELOG (breaking changes, support matrix, migration, known limitations), README, AGENTS.md, api_reference. BREAKING CHANGE: see CHANGELOG.md "Breaking changes", "Removed" and "Migration guide".
- Fork routing: CCV quorum selection, 1.6 destination filter, strict routing with getMessageStatus, per-destination routing (QUEUED until its fork is routed), stamped OffRamp preference, resolver re-check after fork resets, zero token gas overrides, destination fork selected after routing - Register data generated into code; simulator deploys in ~14.4M gas - Register regenerated from the CCIP directory API (134 chains); wrapped native token selection fixed (was GHO on Ethereum, Sepolia, Arbitrum, Base) - Local router: CCIP 2.0 OnRamp/OffRamp rules, unique messageIds, MessageGasLimitTooHigh above uint32 - Hardhat 3 JS helper: 1.6 destination check, decoded execution errors, TypeScript declarations, offline unit tests - npm packaging for Hardhat 3 / Foundry consumers; chainlink-ccip pinned to contracts-ccip-v2.0.0 - CI workflow with tarball consumer smoke test BREAKING CHANGE: see CHANGELOG "Breaking changes" and migration guide.
CCIP Contracts 2.0.0 support in Chainlink Local (V3)
andrejrakic
marked this pull request as ready for review
September 25, 2026 19:35
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chainlink Local V3 (
0.3.0-beta)Breaking release.
CCIPLocalSimulatorandCCIPLocalSimulatorForkmove to@chainlink/contracts-ccip2.0.0 and support the CCV-based lanes that live testnet and mainnet lanes run today, alongside 1.6 and pre-1.6 lanes. The full list of changes and the migration guide are inCHANGELOG.md(Unreleased).Support matrix
@nomicfoundation/hardhat-ethers)Breaking changes (summary)
@chainlink/contracts-ccip1.6.2 -> 2.0.0. Finality moves fromuint16block confirmations to abytes4FinalityCodecconfig (extra args V3, receivers, token pools).lib/chainlink-ccipis pinned to tagcontracts-ccip-v2.0.0.switchChainAndRouteMessagereverts withCCIPLocalSimulatorFork__MessageNotRouted/CCIPLocalSimulatorFork__MessageExecutionFailed(with the decoded reason) instead of logging and continuing.setStrictRouting(false)andgetMessageStatus(messageId)are the opt-out.CCIPLocalRouterreplaces the upstreamMockCCIPRouter(same ABI). Invalid sends now revert with the production error: finality, one token per message, zero amounts,tokenReceiver, receiver CCV config and gas limits aboveuint32. LocalmessageIds are unique per send.getCCVsAndFinalityConfig.evm_version = "cancun"and Foundry >= 1.5.1. The migration guide shows a fork-only profile, so the deployed bytecode is not affected.src/vendor/**removed: sources import@openzeppelin/contracts@4.8.3/@5.3.0and@chainlink/contracts(-ccip)directly.Register: the data is regenerated from the CCIP directory API (134 chains, was 148; routers unchanged).wrappedNativeAddresswas GHO on e.g. Ethereum, Sepolia, Arbitrum and Base, and is now the wrapped native token. Removed chains are listed in the CHANGELOG.NetworkConnection(scripts/CCIPLocalSimulatorFork.js,scripts/data-streams/).The public Solidity API is unchanged:
CCIPLocalSimulator.configuration(), theCCIPLocalSimulatorForkfunction signatures andRegister.NetworkDetails.Highlights
execute, so the production checks run. Owner-configurable verifier resolvers are pointed at a synthetic fork verifier, so live lanes work without mocking.thresholdoptional CCVs).QUEUEDuntil a later call routes them.typeAndVersion, robust to routers that list OffRamps of several versions for a lane.CCIPLocalSimulatorForkdeploys in ~14.4M gas (was ~36M), so it fits on 30M-gas forks.remappings.txtfornode_moduleslayouts)..github/workflows/test.yml): build, fmt, non-fork tests, Hardhat Solidity + JS unit tests, and a tarball consumer smoke test. Fork suites run on manual dispatch once the RPC secrets are configured.Testing
fmt --check, non-fork tests), Hardhat (Solidity + JS unit tests) and consumer smoke all pass.forge test -j 1 --match-path 'test/e2e/**'passes 33/33 andnpm run hardhat-test-jspasses 8/8. They cover Sepolia -> Arbitrum Sepolia, Sepolia <-> Fuji, Ethereum -> Arbitrum One mainnet, 1.6 lanes at pinned blocks, Fast Transfers and CCT token pools.Known limitations
wrappedNativeAddressisaddress(0)on Stable and Botanix, where the API has no unambiguous wrapped native token.The full list is in the CHANGELOG.