ERC-8244: Contract-Hosted Application HTML

I would like to propose a standard way to flag to wallets that an application is self-contained by a smart contract by using a simple view interface:

function html() external view returns (string memory);

The entire HTML document should be returned by calls to this function. Like so.

A few examples “in the wild” of the proposed html() return generating working bytecode dapps that are also complimentary to ERC-4804 and ERC-5219:

Motivation: By confirming that application logic is itself on the blockchain, we can promote safer user interactions with smart contracts.

9 Likes

Love that direction. Would it make sense to reduce the size of the html file using gzip or some other encoding method, and expose the encoding type via an integer?

What if the html file exceeds the 24kb contract size limit? Why not allow to submit the html as chunks.

1 Like

The ERC doesn’t require a specific storage layout, so both optimizations are valid as long as the interface is respected.

See Why not require a specific storage layout Add ERC: Contract-Hosted Application HTML by z0r0z · Pull Request #1712 · ethereum/ERCs · GitHub

3 Likes

Love the work. I have personally being looking to do exactly same for years, and this is a great public good if it does workout. Here are some of my thoughs on your approach

Your existing approach can be improved by

  1. compressing and encoding like what NoWhere does (hostedNoWhere dot com)
  2. user end decoder to stitch multiple contract together to get around the size limit, where will give you unlimited HTML size in theory.

However directly store HTML into contract is a naive approach imo(no offense as I love the project very much), because onchain state are extremely limited and increase Eth node state size is a big industry issue. It is an inefficient hack to host site on Eth contracts while completely ignore we have IPFS built for this(as much as I dont like IPFS solution also). I would love to see a much more elegant solution.

The major road blocks are site specific content and assets, which will never fit into the contract, ignore these will drastically limit the usecase of these HTML sites, imagine Uniswap has to deploy 2 sites one to host their content, one to host swap app.

Host these assets on either AWS or IPFS defeats the purpose of this approach because we might as well host the HTML there.

would love to learn ur thought if you have a solution.

1 Like

Compression techniques will be very useful here. Also, I think it is best to take things one dapp at a time. Not every use case will benefit (for example, social sites or analytics). Onchain HTML is more useful as well for “fallback” and more security-minded instances of protocol UIs. These should be intentionally simple. For example, we have already proven that we can host a DEX aggregator on the blockchain, as well as a reversible payments protocol (end-to-end). So this is not theoretical but more now just a matter of refining the kinds of techniques you mention. Additionally, I think having some duplicative code placed into chunks onchain, such as wallet connection logic, style, etc, that can be called into to build the core dapp elements would alleviate some of the concerns around codesize.

IPFS notably does not offer the same permanence guarantees as a fully onchain instance. Though of course is a step above most dapps today.

2 Likes

I get you. guess our initial intentions are different, I agree with you onchain HTML is useful however limit this to only dapp fallback solution is way under estimate its potential imo. Dapp fallback wise we already have IPFS and interface templates likes Etherscan offers.

If a proper solution is found, onchain powered sites can be the major application of Blockchain and provide information censorship resistance for the whole human society.

might do a protocol type if I can get the solution together.

This actually fits very well with what I’m currently building (XNS Blobs).

With XNS Blobs, you can attach arbitrary data to an XNS name and store it fully on-chain. The system is generic: you specify a file type (e.g. HTML, JS, ZIP), an encoding type (e.g. raw, gzip), and every update is versioned. There’s also a freeze mechanism, which permanently disables further updates, effectively turning the content into an immutable on-chain domain (see the GitHub gist for details).

Contracts can expose html files stored XNS Blobs via the proposed html() function, as suggested here.

Regarding the concern about Ethereum state growth: I think the economic model already addresses this. Storing data on-chain is expensive by design. Deploying a non-trivial HTML app can easily cost hundreds or even thousands of dollars depending on size and gas conditions. That creates a strong natural filter: people will only publish content that they consider finalized and worth persisting.

In that sense, I don’t see IPFS as a complete substitute. It’s great for cheap storage, but it doesn’t provide the same guarantees around permanence and availability. On-chain storage is expensive, but that’s precisely what makes it credible for critical, long-lived content.

https:// gist.github dot com/Walodja1987/8dfc44a70783c43c68297699b3d93c5b

Glad the see XNS names basically added both my two points here - compression and stitching. I think this is pretty much the best solution so far for storing html onchain.

however my argument still stands, this model is a naive approach(again no offense I love both projects), u will hit the asset storage and cost ceilings fast and it will drastically limit the usability of such service to small static html pages which is almost never enough for any projects on modern internet.

I agree IPFS is not the answer, but it is lying somewhere else. Solving this would mean a new exciting primitive for blockchain tech.

1 Like

we can list some use cases you have in mind, in terms of dapps, and we can see what we can do within current constraints. The goal overall is to simply just make use of the blockchain to serve UIs more directly and then converge on the html() method return. Bottlenecks will serve the useful purpose of forcing even more interesting solutions. At least in terms of modern DeFi dapps, they are not that complicated IMO.

2 Likes

Hey, great to see such proposal. I had a look at the ERC and got some feedback on the constraints that I think are worth discussing:

1. External Resource Restrictions

  1. Self-contained. The document MUST NOT reference any external network resource by URL. <script src>, <link href>, <img src>, <iframe src>, <object data>, @import, remote url(), fetch(), and XMLHttpRequest to remote URLs are all forbidden. data: URIs and blob: URIs constructed from in-document strings are permitted.

The spec forbids all external resources, but modern browsers support Subresource Integrity (SRI) with hash-based verification (<script src="..." integrity="sha384-...">). This allows fetching resources while cryptographically ensuring they haven’t been tampered with.

And while today such resources are likely centralised (CDN), some browsers already support ipfs:// URLs via extension or natively, and we can expect more in the future.

Also since the ERC can only prescribe and not enforce, maybe the best is to let authors decide what they fetch.

2. Provider Detection

  1. Wallet provider. The document SHOULD assume an EIP-1193 provider at window.ethereum and MUST NOT depend on any other non-standard browser global.
  1. MUST inject an EIP-1193 provider into the frame and MUST gate every signing request through the host wallet’s normal user-confirmation flow.

The spec assumes window.ethereum (EIP-1193), but EIP-6963 now provides a multi-provider discovery mechanism. Wallets increasingly use this to avoid global conflicts and let users have multiple wallets supported at once.

Should the spec reference EIP-6963 and allow for provider discovery beyond the global?

And maybe be future-proof by not excluding any other or defining what a standard is.

3. Rendering Context

  1. SHOULD render the document in a sandboxed <iframe> with a default-deny Content-Security-Policy. The frame MUST NOT inherit cookies, localStorage, or any credentialed resources from the host page.

Requiring a sandboxed iframe excludes valid rendering contexts like browser extensions or plugins that could render at the top level with appropriate permissions.

Could this be framed as “MUST render with equivalent isolation to a sandboxed iframe” rather than prescribing the iframe itself? But maybe again this is up to the implementer.

4. Network Access

  1. MUST NOT grant the document network access beyond window.ethereum.

The restriction on network access beyond window.ethereum prevents legitimate patterns like embedded IPFS nodes (JS-IPFS) that could fetch content-addressed data while maintaining decentralization guarantees.

I love the premise of constraining to ensure safety, but we should ensure that valid use cases are not considered non-standard. Plus, as mentioned, an ERC can only prescribe, not enforce, and as such I believe whatever an ERC cannot enforce, it should let be. It is fine to add recommendations though.

3 Likes

I agree with you that use cases may be limited due to current constraints. I also so a project that stores data in event logs. It’s also interesting and has potentially less constraints but it relies on indexers. Good to see that people explore different approaches.

I also agree with Ross that constraints may actually allow for creative solutions and innovation. Time will tell.

Talking about innovation, I am wondering whether we’d could create a new language for writing compact, abstract frontend code which then compiles to html. Just describe the bare bone functionality and let compilers / AI do the rest.

I believe the bottle neck is not the HTML code size but the actual customer content and assets, these are not possible to fit onchain in anyway methods, further look lead you to a path to reinvent IPFS.

I am afraid a new DSL is unlikely to be helpful.

1 Like

Nice suggestions, they should make this ERC more future-proof and less restrictive. Feel free to review the PR. feat(erc8244): soften constraints by ndavd · Pull Request #2 · z0r0z/ERCs · GitHub

I reviewed this standard and I think it has a lot of potential. I also think there may be room to push the idea further.

If we think of html() not only as a way to serve one complete HTML document, but as a way for a contract to expose the UI for its own domain, then each contract can become a self-contained service.

For example:

AccountManagementContract
├── html() => account UI
└── state/actions => account state and logic

In this model, the HTML returned by the contract is not just static content. It can read from and write to the contract that hosts it, or interact with other contracts through the injected provider. This makes the contract behave more like a complete web service without requiring a traditional backend.

This can be extended further with a higher-level registry/factory contract that acts as the composition root for a larger application system.

For example:

RegistryContract
   ├── AccountManagementContract
   │      ├── html() => account UI
   │      └── state/actions => account state and logic
   │
   ├── TradeManagementContract
   │      ├── html() => trading UI
   │      └── state/actions => trading state and logic
   │
   ├── GovernanceManagementContract
   │      ├── html() => governance UI
   │      └── state/actions => voting/proposal state and logic
   │
   └── TreasuryManagementContract
          ├── html() => treasury UI
          └── state/actions => treasury state and logic

This is different from simply splitting one large HTML file into chunks. Instead, each contract represents a domain or service, and each service hosts its own UI through html().

The registry contract can then provide service discovery for the whole system. A client can query the registry, discover the available service contracts, fetch their HTML interfaces, and render or route between them.

At the client layer, the renderer can also use the registry as a routing table. For example, the registry can map paths or route identifiers to service contracts. The client reads the registry, resolves the requested path, fetches the corresponding service contract’s html(), and renders it.

/account     => AccountManagementContract.html()
/trade       => TradeManagementContract.html()
/governance  => GovernanceManagementContract.html()
/treasury    => TreasuryManagementContract.html()

This makes the registry not only a service discovery layer, but also an on-chain routing layer for contract-hosted applications.

The registry can also support lazy loading. A client does not need to fetch every service UI upfront. The registry can map page IDs or paths to service contracts.

For example:

// page 0 => homepage
// page 1 => vote
// page 2 => treasury
mapping(uint256 => address) public pages;

Then the client can choose its own loading strategy.

Full load:
- read all registered pages
- fetch all html()
- render the full application shell

Lazy load:
- read only the requested page ID or path
- fetch only that contract's html()
- render it when needed

This means both partial loading and full loading are possible. The registry only defines the application topology, while the client decides how much of the application to load at a time.

This gives a few benefits:

  • It reduces practical HTML size limitations because the application is separated across multiple domain contracts.
  • It makes large contract-hosted applications easier to organize and reason about.
  • It allows each domain contract to own both its state/actions and its own UI.
  • It creates a single source of trust through the registry contract.
  • If the registry is made immutable, the application topology can also become immutable.
  • It allows clients to implement routing based on registry-defined paths, making larger contract-hosted applications feel closer to normal multi-page web applications.
  • It supports lazy loading, so clients can load only the page or service needed instead of fetching the whole application upfront.

So the base html() interface can stay simple, while a registry-based abstraction could support larger contract-hosted application systems, almost like on-chain microservices or contract-hosted micro-frontends.