For the complete documentation index, see llms.txt. This page is also available as Markdown.

On-Chain Data URLs and ENS Hooks

In addition to distributed storage networks like IPFS/IPNS, Arweave/ArNS, and Swarm, eth.limo supports a storage backend where your content lives directly on the blockchain: on-chain data URLs, published through EIP-8121 hooks.

Instead of pointing to a hash on a storage network, the ENS contenthash contains a hook — a fully self-describing smart contract call. When a compatible client (such as the eth.limo gateway) resolves the name, it executes that call and serves the returned data URL as the website content. No pinning services, no storage deals, no separate upload step — the chain itself is the storage layer.

How It Works

An ENS contenthash normally encodes a storage protocol and an identifier (an IPFS CID, an Arweave transaction ID, a Swarm hash). With this backend the contenthash instead carries one of two payloads:

Payload
Protocol code
Description

EIP-8121 hook

0x30009b

A smart contract call that returns the content

Plain data URI

0x3000f2

The data URL embedded directly in the contenthash

A hook fully specifies what function to call, with what parameters, on which contract, on which chain:

  • the function signature with explicit types (e.g. data(bytes32))

  • the function call with its values in human-readable form (e.g. data(0x1234...))

  • the return type for ABI decoding ((bytes) for content resolution)

  • the target — an ERC-7930 interoperable address identifying both the contract and the chain it lives on

Because a hook is completely self-describing, any client can resolve it without external documentation. Resolution requires an ERC-3668 (CCIP-Read) enabled provider, which also makes cross-chain resolution possible: an ENS name on Ethereum mainnet can serve content from a contract on another EVM chain.

The resolution flow is:

  1. Client reads the contenthash for the ENS name and detects the hook protocol code.

  2. Client decodes the hook and (optionally) verifies the target contract against a trusted list.

  3. Client executes the specified contract call on the specified chain — for example data(nodehash) on a DataResolver contract.

  4. The returned bytes are decoded as a data URL (data:text/html;base64,...) and served as the website.

When To Choose This Backend

Consideration
On-chain Data URLs
Distributed storage (IPFS/Arweave/Swarm)

Availability

As available as the chain's RPC layer — no pinning or gc

Depends on pinning, endowment, or postage stamps

Content size

Small artifacts only (~5KB is a practical target)

MBs to GBs

Cost

Gas for contract storage (expensive per byte)

Cheap or free per byte

Updates

Update contract state; contenthash can stay the same

New CID/transaction per revision (unless IPNS/ArNS)

Trust model

Verifiable contract call; client-side trust lists

Content addressing (hash verifies bytes)

Cross-chain

Yes — target any EIP-155 chain

N/A

This makes on-chain data URLs a good fit for small, high-value artifacts: profile pages, on-chain "link in bio" microsites, credential and registry data, or metadata that other contracts need to reference — and a poor fit for media-heavy dWebsites, which should stay on IPFS, Arweave, or Swarm.

Hooks support functions with 0–2 parameters of fixed-size Solidity primitives:

  • 0 parametersgetData() — global/singleton data shared by all names pointing at the contract

  • 1 parameterdata(bytes32 nodehash) — per-name data keyed by the ENS nodehash

  • 2 parametersdata(bytes32 nodehash, bytes32 hashOfContent) — per-name data with cache-busting, where the second parameter is a hash of the expected content (integrity) or an autoincrement value (gateway cache invalidation)

Trust Verification

One of the most important features of hooks is that clients can evaluate whether they trust the target contract before calling it. Resolvers can maintain trusted-target lists (per chain and address) and refuse or warn on hooks pointing at unknown contracts. This matters most when hooks are used for credential-style data (Proof-of-Personhood, KYC registries), but applies to content resolution too.

Implementation Scope

The ethlimo/ens-hooks library — used by the eth.limo gateway — is a focused implementation for contenthash resolution. Its intentional restrictions:

  • 0–2 parameters of fixed-size primitives and strings (bool, address, uintN, intN, bytesN, string ≤ 512 chars); no dynamic bytes, arrays, structs, or tuples

  • bytes return type only

  • EIP-155 (EVM) chains only

  • No recursive resolution — hooks cannot point to other hooks

See the library's LIMITATIONS notes for the full list and rationale.

Publishing Content

Publishing is a three-step encoding flow, with tooling provided by the ens-hooks repository:

  1. Artifact bytes → base64 data URL string

  2. Hook metadata (target contract, chain, function) → EIP-8121 hook bytes

  3. Hook bytes → ENS contenthash bytes

You then deploy (or reuse) a DataResolver-style contract, store the data URL on chain with setData(node, dataUrl), and set the contenthash on your ENS name. Remember: on-chain storage is priced in gas, so keep artifacts to roughly 5KB or less.

For the complete step-by-step walkthrough — including the encode-full one-shot command and Sepolia testnet deployment — follow the ens-hooks encoding guide.

Resolving Content

The eth.limo gateway stack resolves hook-based contenthashes automatically through its Data URL server. To test end-to-end (including against testnets), the easiest path is running a local gateway, which ships with the Data URL server enabled by default.

Resources

Last updated