Skip to main content

CCIP Read

CCIP Read, also referred to by its full name Cross-chain Interoperability Protocol, is a protocol developed by Chainlink to enable applications to access cross-chain (i.e. on an L2) or offchain data through a gateway server.

Ethereum Name Service (ENS) implements CCIP Read so that it can resolve domains on Ethereum even if the relevant data and records are stored on an L2 chain — an offchain resolver framework. The process is trust-minimal, as the gateway returns a storage proof that can be verified on L1, and which is immune from any intervention or tampering.

Linea adapted the functionality of the relevant ENS contract, evm-gateway, so that it would function correctly with Linea's Sparse Merkle Tree design. The Linea ENS repository therefore adds support for CCIP Read to Linea.

info

Read more about Sparse Merkle Trees in our architecture documentation.

Linea ENS​

Linea ENS allows Linea users to register human-readable domains for considerably lower fees than on Ethereum Mainnet, it also leverages smart contracts with considerable utility for developers by enabling CCIP Read. See the user guide here.

The Linea ENS repository contains a frontend and associated contracts for ENS to work on Linea, including functionality that enables any L1 application to query L2 data using CCIP Read.

However, Linea ENS is just one example use case; CCIP Read is beneficial in any scenario where it's advantageous to store data on L2. In addition to ENS-like systems, the library enables L1 applications to create allowlists that use cross-chain Verax attestations to determine eligibility for NFT mints or governance voting, for example.

Use CCIP Read on Linea​

While the repository was created to bring ENS to Linea, its component libraries can be applied in other contexts.

The two main contracts are:

  • The Linea CCIP Gateway, which implements the gateway required for L1 protocols to query data on Linea, and;
  • The Linea State Verifier, which verifies the state proofs generated by the gateway.

Together, they can be used to retrieve data on Linea in a trustless way. They are 'generic' in the sense that they can be applied to any use case.

The basic outline for using these contracts in a read capacity is as follows:

  1. Your L1 contract, which we'll refer to as the client contract, requires data from L2. To do this, it builds a request to the EVMFetcher contract (part of the Linea State Verifier).

  2. The EVMFetcher contract uses CCIP Read to call the offchain gateway.

  3. The gateway retrieves the requested information from your associated L2 contract.

  4. The gateway returns data and proofs to the IEVMVerifier contract.

  5. IEVMVerifier and LineaSparseProofVerifier verify the proofs and return the data to the L1 client contract.

Example​

The below contract fetches another contract's storage value, testUint.

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

import { EVMFetcher } from '@ensdomains/evm-verifier/contracts/EVMFetcher.sol';
import { EVMFetchTarget } from '@ensdomains/evm-verifier/contracts/EVMFetchTarget.sol';
import { IEVMVerifier } from '@ensdomains/evm-verifier/contracts/IEVMVerifier.sol';

contract TestL2 {
uint256 testUint; // Slot 0

constructor() {
testUint = 42;
}
}

contract TestL1 is EVMFetchTarget {
using EVMFetcher for EVMFetcher.EVMFetchRequest;

IEVMVerifier verifier;
address target;

constructor(IEVMVerifier _verifier, address _target) {
verifier = _verifier;
target = _target;
}

function getTestUint() public view returns(uint256) {
EVMFetcher.newFetchRequest(verifier, target)
.getStatic(0)
.fetch(this.getSingleStorageSlotCallback.selector, "");
}

function getSingleStorageSlotCallback(bytes[] memory values, bytes memory) public pure returns(uint256) {
return uint256(bytes32(values[0]));
}
}