{"id":"ERC-3668","name":"CCIP Read (Offchain Data Retrieval)","status":"Final","chain":"both","category":{"id":"comprehension","name":"Comprehension & Display","description":"Making blockchain data readable to humans"},"journeyStages":[{"id":"asset-display","name":"Asset Discovery & Display","description":"Seeing tokens, NFTs, and balances"},{"id":"reading","name":"Reading & Understanding","description":"Interpreting what you're being asked to do"}],"uxImpact":"ERC-3668 defines a client lookup protocol in which a contract can return gateway URLs and a client can fetch offchain data, then submit the response for contract verification. This can make ENS or other records resolve through gateways, but clients still need support, privacy disclosure, timeout/retry handling, and a trust policy for the source.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-3668","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-3668","sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-3668","type":"official-spec"}],"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","summary":"ERC-3668 defines a client lookup protocol in which a contract can return gateway URLs and a client can fetch offchain data, then submit the response for contract verification. This can make ENS or other records resolve through gateways, but clients still need support, privacy disclosure, timeout/retry handling, and a trust policy for the source.","designerTakeaways":["Show loading and timeout states during supported offchain lookups without requiring CCIP terminology in the primary copy.","Expose the gateway source, proof status, and privacy context when the lookup affects user decisions.","Offer retry or onchain fallback when a gateway or client cannot complete the lookup."],"applicability":{"whenToUse":["The selected contract exposes a CCIP Read lookup and the client supports the protocol.","The product can explain loading, gateway source, proof validation, privacy, and retry behavior.","The app has a fallback when a gateway or offchain response is unavailable."],"whenToAvoid":["All required data is already onchain and an offchain lookup adds no value.","Gateway infrastructure is unreliable and the product has no fallback or timeout path.","Privacy requirements forbid the client from contacting the configured gateway."]},"prototypeFirst":[{"screen":"Resolved lookup","why":"Show the normal client lookup only when the contract and client support CCIP Read.","covers":["Gateway request","Verified response"],"include":["Loading indicator","Source disclosure","Resolved result"]},{"screen":"Unsupported or unavailable lookup","why":"Clients, contracts, gateways, or networks may not support the lookup path.","covers":["Unsupported client","Gateway unavailable"],"include":["Plain-language reason","Fallback action"]},{"screen":"Gateway failure and retry","why":"A failed offchain request should explain what the client can do next.","covers":["Timeout","Invalid or unverified response"],"include":["Retry path","Support context"]},{"screen":"Source and privacy disclosure","why":"Users may need to understand where a value came from and what the lookup reveals.","covers":["Gateway URL","Proof status","Lookup context"],"include":["Expandable section","Copy buttons","Explorer link"]}],"mentalModel":[{"label":"Onchain request","description":"A contract call triggers a revert with instructions to fetch data that is not stored onchain."},{"label":"Gateway fetch","description":"The client calls a gateway URL from the response. The lookup can be quiet in the default path, but source and privacy disclosure should be available when relevant."},{"label":"Verified response","description":"The gateway returns data plus proof the contract can verify. Failed gateways need retry copy, not opaque errors."},{"label":"Transparent resolution","description":"The client submits the response for contract verification, then renders the result or a clear unverified/error state."},{"label":"Source disclosure","description":"Advanced views can show gateway source, proof status, timeout, and privacy context; do not imply that offchain lookup is always invisible or instant."}],"statesToDesign":[{"state":"Lookup requested","trigger":"A supported client receives an OffchainLookup instruction from the contract.","userNeed":"Know that data is being fetched before the result is shown.","designResponse":"Show loading, gateway, privacy, and timeout context without treating the result as verified yet."},{"state":"Response validation","trigger":"The client has a gateway response and submits it to the callback for contract verification.","userNeed":"Know whether the response passed the contract check.","designResponse":"Keep the result unverified until the callback succeeds; show source and proof state."},{"state":"Gateway unavailable","trigger":"The configured gateway times out, rejects the request, or returns malformed data.","userNeed":"Recover without losing the original action.","designResponse":"Offer retry, another configured gateway, or an onchain fallback when available."},{"state":"Response verified","trigger":"The contract callback accepts and verifies the returned data.","userNeed":"Use the resolved record with clear source context.","designResponse":"Show the resolved value, verification result, source, and any privacy note."},{"state":"Response rejected","trigger":"The callback or proof check rejects the gateway response.","userNeed":"Know whether the value can be trusted or retried.","designResponse":"Keep the unverified value out of the primary path and offer a retry or fallback."}],"designDecisions":[{"question":"When do users see gateway details?","recommendation":"Keep the normal result readable and disclose source, proof, privacy, and timeout information when it affects trust.","rationale":"CCIP Read moves a lookup off chain, so the source and validation boundary matter."},{"question":"What happens when a lookup cannot resolve?","recommendation":"Show the failure state and offer retry or a supported onchain fallback.","rationale":"The contract, client, gateway, and proof step can fail independently."},{"question":"How do you label a resolved value?","recommendation":"Show whether the callback verified the response and where it came from.","rationale":"A gateway response is not trusted solely because it loaded successfully."}],"problemsSolved":[{"problem":"Contracts may need data outside their onchain state","oldWay":"Each client invents a gateway request and response flow","newWay":"A compatible client can follow the contract's lookup instructions and submit the returned data for verification","impact":"high"},{"problem":"Gateway delays and failures are hard to explain","oldWay":"A lookup fails as an opaque contract or network error","newWay":"The client can show loading, timeout, gateway, proof, and fallback states","impact":"high"},{"problem":"Offchain source and privacy boundaries are unclear","oldWay":"The client hides where a lookup went and whether the response was verified","newWay":"The UI can disclose source, validation status, and a privacy-aware fallback","impact":"medium"}],"uxPatterns":[{"name":"Resolving Indicator","description":"Loading state while a supported client performs a CCIP Read lookup.","components":["ResolveSpinner","RetryButton"],"userFlow":["User triggers lookup","Spinner shows","Data resolves or retries","Result displayed"]},{"name":"Gateway Fallback","description":"Retry or use a configured alternate gateway without bypassing contract verification.","components":["FallbackGateway","ErrorBanner"],"userFlow":["Primary gateway fails","App tries alternate","User sees progress","Success or clear error"]}],"antiPatterns":[{"pattern":"Hiding an offchain lookup or validation failure","why":"Users cannot tell whether the problem is the contract, gateway, client, or proof","instead":"Show loading, source, proof status, timeout, and fallback states before the result is used","severity":"critical"},{"pattern":"Treating offchain data as automatically trusted","why":"A gateway response still needs contract verification and a privacy-aware source policy","instead":"Use outcome language with source, proof, and privacy disclosure available","severity":"high"},{"pattern":"No fallback when client or gateway lacks support","why":"The value cannot resolve even though the contract may be valid","instead":"Explain the limitation and offer a supported client, retry, or onchain fallback","severity":"high"}],"vocabulary":[{"use":"Loading from a supported source","avoid":"Verified everywhere","why":"The client can validate a response for this contract without making a universal trust claim."},{"use":"Lookup unavailable","avoid":"Gateway reverted","why":"Describe the user-visible recovery state without exposing an implementation error as the cause."},{"use":"Show source and privacy details","avoid":"The lookup is invisible","why":"Users may need to understand what the gateway receives and what the contract verifies."}],"specConstraints":[{"id":"offchain-validation","constraint":"Offchain data lookups are transparent to clients, but the validation contract defines how returned data is checked.","appliesTo":"application","normativity":"normative","specQuote":"`urls` specifies a list of URL templates to services (known as gateways) that implement the CCIP read protocol and can formulate an answer to the query. `urls` can be the empty list `[]`, in which case the client MUST specify the URL template. The order in which URLs are tried is up to the client, but contracts SHOULD return them in order of priority, with the most important entry first.","specSection":"Specification > Contract interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#contract-interface","designImplication":"Show an explicit resolving/validation state and identify the contract-specific trust/validation path instead of treating gateway data as self-authenticating."},{"id":"gateway-fallback","constraint":"Clients must handle gateway HTTP errors and continue through the configured fallback behavior.","appliesTo":"application","normativity":"normative","specQuote":"If the URL template does not contain the `{data}` substitution parameter, the client MUST send a POST request after replacing the substitution parameters as described above. The POST request MUST be sent with a Content-Type of `application/json`, and a payload matching the following schema:","specSection":"Specification > Gateway Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#gateway-interface","designImplication":"Design retry, alternate-gateway, and unresolved states; distinguish an HTTP failure from a validation failure."},{"id":"lookup-limit","constraint":"Clients should limit the number of CCIP Read lookups for one request.","appliesTo":"application","normativity":"normative","specQuote":"Unsuccessful requests MUST return the appropriate HTTP status code - for example, 404 if the `sender` address is not supported by this gateway, 400 if the `callData` is in an invalid format, 500 if the server encountered an internal error, and so forth. If the Content-Type of a 4xx or 5xx response is `application/json`, it MUST adhere to the following JSON schema:","specSection":"Specification > Gateway Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#gateway-interface","designImplication":"Set a visible resolution budget and give users a clear unresolved result when recursive lookups exceed it."},{"id":"non-json-error","constraint":"Clients must not assume an HTTP error body is JSON.","appliesTo":"application","normativity":"normative","specQuote":"A client that supports CCIP read MUST make contract calls using the following process:","specSection":"Specification > Client Lookup Protocol","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#client-lookup-protocol","designImplication":"Use a generic but informative network-error state when a gateway returns non-JSON content and never expose a parser failure as the primary explanation."},{"id":"gateway-response-validation","constraint":"Gateway responses must be checked against the request context and validation contract.","appliesTo":"application","normativity":"security-considerations","specQuote":"Contracts must also implement sufficient validation of the data returned by the gateway to ensure it is valid. The validation required is application-specific and cannot be specified on a global basis. Examples would include verifying a Merkle proof of inclusion for an L2 or other Merkleized state, or verifying a signature by a trusted signer over the response data.","specSection":"Security Considerations > Gateway Response Data Validation","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#gateway-response-data-validation","designImplication":"Show unresolved or invalid-data states separately from successful resolution and keep the gateway response bound to the requested name/record."},{"id":"callback-recheck","constraint":"A callback must repeat the relevant authorization and sanity checks before using returned data.","appliesTo":"account-implementation","normativity":"security-considerations","specQuote":"### Client Extra Data Validation\nIn order to prevent a malicious client from causing unintended effects when making transactions using CCIP read, contracts MUST implement appropriate checks on the `extraData` returned to them in the callback. Any sanity/permission checks performed on input data for the initial call MUST be repeated on the data passed through the `extraData` field in the callback. For example, if a transaction should only be executable by an authorised account, that authorisation check MUST be done in the callback; it is not sufficient to perform it with the initial call and embed the authorised address in the `extraData`.","specSection":"Security Considerations > Client Extra Data Validation","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#client-extra-data-validation","designImplication":"Treat callback completion as a second validation boundary; do not let a UI or relayer mark data final merely because a gateway responded."},{"id":"privacy-and-fingerprinting","constraint":"External gateway requests can reveal lookup activity and should be considered in privacy and fingerprinting design.","appliesTo":"application","normativity":"security-considerations","specQuote":"Contracts must also implement sufficient validation of the data returned by the gateway to ensure it is valid. The validation required is application-specific and cannot be specified on a global basis. Examples would include verifying a Merkle proof of inclusion for an L2 or other Merkleized state, or verifying a signature by a trusted signer over the response data.","specSection":"Security Considerations > Gateway Response Data Validation","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#gateway-response-data-validation","designImplication":"Disclose external lookup behavior where relevant and provide privacy-preserving defaults or an explicit unresolved state when the user declines."},{"id":"transaction-ccip-opt-in","constraint":"Transaction-triggered CCIP Read should be disabled by default and require explicit opt-in.","appliesTo":"wallet","normativity":"security-considerations","specQuote":"In order to prevent a malicious client from causing unintended effects when making transactions using CCIP read, contracts MUST implement appropriate checks on the `extraData` returned to them in the callback. Any sanity/permission checks performed on input data for the initial call MUST be repeated on the data passed through the `extraData` field in the callback. For example, if a transaction should only be executable by an authorised account, that authorisation check MUST be done in the callback; it is not sufficient to perform it with the initial call and embed the authorised address in the `extraData`.","specSection":"Security Considerations > Client Extra Data Validation","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#client-extra-data-validation","designImplication":"Wallet confirmation must disclose that a transaction may trigger external lookups and provide an explicit user-controlled opt-in."},{"id":"gateway-url-scheme","constraint":"Returned gateway URLs may use any schema, but the client specification only defines handling for HTTPS URLs.","appliesTo":"application","normativity":"normative","specQuote":"The URLs returned by a contract may be of any schema, but this specification only defines how clients should handle HTTPS URLs.","specSection":"Specification > Gateway Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#gateway-interface","designImplication":"Treat non-HTTPS gateway routes as unsupported or explicitly reviewed, and show the source scheme when an offchain lookup affects a user decision."},{"id":"lookup-count-bound","constraint":"Clients must cap recursive lookup attempts, with a recommended minimum limit of four per contract call.","appliesTo":"application","normativity":"normative","specQuote":"This protocol can result in multiple lookups being requested by the same contract. Clients MUST implement a limit on the number of lookups they permit for a single contract call, and this limit SHOULD be at least 4.","specSection":"Specification > Client Lookup Protocol","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#client-lookup-protocol","designImplication":"Show a bounded lookup/retry state and explain when recursive resolution has reached the client's safety limit."},{"id":"ccip-client-overrides","constraint":"Client libraries should provide an override hook for CCIP Read calls and make domain allowlists or blocklists easy to manage.","appliesTo":"application","normativity":"security-considerations","specQuote":"Client libraries should provide clients with a hook to override CCIP read calls - either by rewriting them to use a proxy service, or by denying them entirely. This mechanism or another should be written so as to easily facilitate adding domains to allowlists or blocklists.","specSection":"Security Considerations > HTTP requests and fingerprinting attacks","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#http-requests-and-fingerprinting-attacks","designImplication":"Give users or operators a clear proxy, deny, allowlist, and blocklist control instead of making every contract-supplied domain unavoidable."},{"id":"transaction-ccip-default-off","constraint":"Client libraries should disable CCIP Read for transactions by default and require explicit enablement.","appliesTo":"wallet","normativity":"security-considerations","specQuote":"Client libraries should disable CCIP read for transactions (but not for calls) by default, and require the caller to explicitly enable this functionality. Enablement should be possible both on a per-contract, per-domain, or global basis.","specSection":"Security Considerations > HTTP requests and fingerprinting attacks","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#http-requests-and-fingerprinting-attacks","designImplication":"Separate read-time lookup support from transaction-time external effects and show the scope of any user-approved enablement."},{"id":"wallet-ccip-proxy","constraint":"Wallets fetching user information should disable transaction CCIP Read and omit from addresses, or route lookups through a user proxy.","appliesTo":"wallet","normativity":"security-considerations","specQuote":"Wallet authors that are responsible for fetching user information - for example, by querying token contracts - should either ensure CCIP read is disabled for transactions, and that no contract calls are made with a 'from' address supplied, or operate a proxy on their users' behalf, rewriting all CCIP read calls to take place via the proxy, or both.","specSection":"Security Considerations > HTTP requests and fingerprinting attacks","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#http-requests-and-fingerprinting-attacks","designImplication":"Make wallet privacy mode and proxy routing visible when resolving user data; do not silently send identifying from-address requests to contract-controlled gateways."},{"id":"unsupported-client","constraint":"Contracts using CCIP Read are not transparently compatible with clients that do not implement the mechanism.","appliesTo":"application","normativity":"backwards-compatibility","specQuote":"Contracts that require CCIP read will not function in conjunction with clients that do not implement this specification.","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-3668#backwards-compatibility","designImplication":"Provide a recognizable unsupported-client state and an alternate path where product requirements demand access without CCIP Read support."}],"technicalNotes":"ERC-3668 lets a contract return a OffchainLookup-style instruction containing gateway URLs, call data, callback information, and extra data. A client fetches the gateway response and submits it back through the callback; the contract verifies the response according to its own implementation. Clients should expose loading, timeout, gateway, proof, and privacy states and must not bypass verification or treat a gateway response as universally trusted.","relatedStandards":[{"id":"ERC-137","relationship":"ENS resolution uses CCIP Read"},{"id":"EIP-695","relationship":"Network identity and endpoint trust remain separate from offchain lookup validation"}]},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-3668","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-3668","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-3668","markdown":"https://www.eipsfordesigners.com/standards/ERC-3668/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-3668/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-3668","official":"https://ercs.ethereum.org/ERCS/erc-3668","discussion":"https://ethereum-magicians.org/search?q=ERC-3668"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}