// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {Chain138MainnetCheckpoint} from "../../contracts/mainnet-checkpoint/Chain138MainnetCheckpoint.sol"; import {CheckpointStorage} from "../../contracts/mainnet-checkpoint/storage/CheckpointStorage.sol"; import {CheckpointLeaf} from "../../contracts/mainnet-checkpoint/libraries/CheckpointLeaf.sol"; import {CheckpointFlags} from "../../contracts/mainnet-checkpoint/libraries/CheckpointFlags.sol"; import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol"; import {CheckpointHubConfig} from "../../contracts/mainnet-checkpoint/libraries/CheckpointHubConfig.sol"; contract MockCcipRouter { function deliver(address receiver, IRouterClient.Any2EVMMessage calldata message) external { Chain138MainnetCheckpoint(receiver).ccipReceive(message); } } contract CcipReceiveTest is Test { Chain138MainnetCheckpoint hub; MockCcipRouter router; address emitter = address(0xE01); address admin = address(0xA11CE); function setUp() public { router = new MockCcipRouter(); Chain138MainnetCheckpoint impl = new Chain138MainnetCheckpoint(); bytes memory initData = abi.encodeCall( Chain138MainnetCheckpoint.initialize, (admin, address(router), uint64(138_0001), emitter) ); hub = Chain138MainnetCheckpoint(address(new ERC1967Proxy(address(impl), initData))); vm.startPrank(admin); CheckpointHubConfig.HubConfig memory cfg = CheckpointHubConfig.mainnetDefaults(); cfg.enforcePreviousBatchId = false; cfg.requireValidatorSigs = false; cfg.allowCCIPIngress = true; cfg.ccipRouter = address(router); cfg.batchEmitterOnSource = emitter; hub.applyConfig(cfg); vm.stopPrank(); } function testCcipReceiveSubmitsBatch() public { CheckpointLeaf.PaymentLeafV1 memory leaf = CheckpointLeaf.PaymentLeafV1({ txHash: keccak256("ccip-tx"), from: address(1), to: address(2), value: 1 ether, blockNumber: 50, blockTimestamp: 1, gasUsed: 21000, success: true }); bytes32 root = CheckpointLeaf.paymentLeafV1(138, leaf); CheckpointStorage.CheckpointHeader memory header = CheckpointStorage.CheckpointHeader({ batchId: 1, previousBatchId: 0, chainId: 138, checkpointBlock: 50, startBlock: 50, endBlock: 50, blockHash: keccak256("bh"), stateRoot: keccak256("sr"), paymentsRoot: root, receiptsRoot: bytes32(0), txCount: 1, flags: CheckpointFlags.PARTIAL_BATCH, submittedAt: 0, submitter: address(0), contentURI: bytes32(0) }); bytes32[] memory hashes = new bytes32[](1); hashes[0] = leaf.txHash; bytes memory payload = abi.encode(header, hex"01", hashes, bytes32(0), abi.encode(leaf)); IRouterClient.Any2EVMMessage memory message = IRouterClient.Any2EVMMessage({ messageId: keccak256("mid"), sourceChainSelector: 138_0001, sender: abi.encode(emitter), data: payload, tokenAmounts: new IRouterClient.TokenAmount[](0) }); router.deliver(address(hub), message); assertEq(hub.getLatestBatchId(), 1); } }