This repository contains the code for a Chrome extension implementing the ERC-4804 URLs (web3://
URLs). This can be used to create censorship-resistant websites hosted directly on EVM-compatible blockchains such as Ethereum.
The extension supports most ERC-4804 websites.
web3://w3eth.eth
web3://vitalikblog.eth
web3://app-uniswap-org.eth
web3://art-blocks-io.eth/render/78/0
- The extension intercepts all HTTP requests to
*.w3eth.io
and*.w3link.io
and redirects them to its own service worker. The HTTP request to*.w3eth.io
never actually makes it onto the network.
- You can type in
web3://
URLs in the address bar, so long as you writeweb3
("web3" and then pressing the spacebar) instead of theweb3://
URL prefix.
- You can paste in
web3://
URLs, so long as you writeweb3
("web3" and then pressing the spacebar) before pasting it in the URL bar.
This extension abides by the Manifest v3 extension specification, despite Manifest v3's restrictions against blocking HTTP request interception. This comes with significant limitations relative to a browser with native web3://
support, such as evm-browser. The aim of this Chrome extension should be to act as a bridge for introducing users to the power of web3://
URLs, in the hope that they are convinced to install a fully-featured browser.
The largest user-visible limitation is that the URL in the address bar starts with chrome-extension://{EXTENSION_ID}
instead of showing the plain web3://
URL. This is unfortunately unavoidable within Chrome. However, it may be possible to improve this somewhat by having the main extension frame show a secondary address bar with the web3://
URL shown in the textbox, and embed the actual web3://
page in a sub-frame. This "banner"-like textbox could also be a place to advertise the existence of evm-browser for a fully-fledged experience.
The web3 pages need to be served in a chrome-extension
context in order to have access to service workers, as extension sandbox pages do not have service worker functionality. However, chrome-extension
pages have a restricted Content-Security-Policy
which prevents them from loading <script>
tags coming from non-extension pages, as well as executing inline <script>
tags. To work around this limitation, the extension needs to rewrite HTML pages to fetch and serve the scripts by itself. This rewriting is brittle.
For the same reason, connecting to web3 wallets (like Metamask) via window.ethereum
also does not work, because this object is not injected on chrome-extension
pages. It may be possible to implement this by implementing a shim object that does get injected and that relays commands back and forth using extension message passing.
Integrating this extension's code within existing browser wallet extensions would also be unwise. This is because this extension executes remotely-fetched JavaScript within a chrome-extension
context. This is OK because the extension only requests a very limited set of capabilities and tries to mitigate access to the Chrome APIs. Chrome also does a good job to isolate extensions from each other, so even if foreign JavaScript code were to take over the chrome-extension
origin, the extent of its damage would be limited. However, a web3 wallet extension needs a lot more permissions to function, so it would be unsafe to use this technique within other wallet extensions as they stand.
With Manifest v3, Google has rescinded support for the webRequestBlocking
permission. This would have been the perfect API to intercept HTTP gateway requests and implement an extension that way, but this API is no longer available in Manifest v3. So this extension does the next best thing.
The extension works by creating an extension service worker that listens for requests on its chrome-extension://{EXTENSION_ID}
endpoint. Additionally, it uses the declarativeNetRequest
API with redirect
rules in order to redirect traffic to w3link gateways and its own fictitious *.web3
gateway to its own chrome-extension://{EXTENSION_ID}
endpoint.
When receiving a request, the extension performs a web3 fetch request using the web3protocol
library. It then inspects the mime-type and the body of the response to see if it looks like HTML. If it does, it rewrites several parts of the HTML in order to make it work within a chrome-extension
page context. Specifically:
- A
<base href="https://{CONTRACT_ADDRESS}.web3:{CHAIN_ID}/{PATH}" />
tag is added in the<head>
of the page. This is necessary in order to make all relative and absolute URLs in other parts of the page (<img>
tags,<a>
links, etc) point to the right location, rather than thechrome-extension
page. Since requests to the fictitious*.web3
TLD are redirected back to the extension, this does not cause any requests or DNS lookups to leak onto the network. - All
<link>
URLs that are not stylesheets or prefetched scripts are deleted. This prevents some errors from caused by theContent-Security-Policy
directive ofchrome-extension
pages. - All
<script>
tags are rewritten. There are two types of<script>
tags:- Scripts that import code from other URLs (
<script src="{SCRIPT_URL}"></script>
): Since theContent-Security-Policy
of Chrome extension pages does not allow fetching external scripts, thesrc
attribute gets rewritten tochrome-extension://{EXTENSION_ID}/web3scripturl://{base64(SCRIPT_URL)}
. The extension service worker knows to handle requests starting withweb3scripturl://
as requests for fetching remote JavaScript code. - Inline scripts (
<script>{SCRIPT_CODE}</script>
): Since theContent-Security-Policy
of Chrome extension pages does not allow executing inline scripts, the{SCRIPT_CODE}
section of the<script>
tag is removed, and instead the<script>
tag is rewritten as<script src="chrome-extension://{EXTENSION_ID}/web3scriptinline://{base64(SCRIPT_CODE)}"></script>
. The extension service worker knows to handle requests starting withweb3scriptinline://
as requests for serving inline JavaScript code.
- Scripts that import code from other URLs (
- A
<script>
tag is added before all other<script>
tags, which resolves some incompatibilities due to being served from achrome-extension
origin. This also takes care of rewriting the URLs of deferred loading of future scripts.
The extension also adds "web3" as an omnibox keyword. This allows users to interact with it by typing "web3" and then pressing the spacebar in the browser's address bar. This enables the user to type or paste in web3://
URLs in the address bar, albeit with the extra burden of activating the omnibox keyword.
This extension is not yet safe for widespread use. Many functions that should enforce cross-origin isolation currently do not properly do this. This extension still fails to display some web3://
sites properly. Until all of this is fixed, it will not be available on the Chrome web store. You may still compile it manually and use it for demonstration purposes.
The extension uses TypeScript for its service worker, Webpack for compiling it all, and Yarn as a build and package management tool.
On Debian:
# Install Yarn as per https://classic.yarnpkg.com/lang/en/docs/install/#debian-stable
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt update && sudo apt install yarn
$ yarn --version
1.22.19
# Install Node.js v20 as per https://github.com/nodesource/distributions#using-debian-as-root
# Warning: this executes a shell script as root from deb.nodesource.com!
$ curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - && sudo apt-get install -y nodejs
$ node --version
v20.4.0
# Check out this repository.
$ git clone --recursive https://github.com/ComfyGummy/chrome-web3
$ cd chrome-web3
# Install dependencies via yarn.
$ yarn install --frozen-lockfile
# Build:
$ yarn build
If you have followed these instructions, the Chrome extension files are now in the dist/chrome-web3-extension
subdirectory of the repository. You can load it into Chrome by enabling developer mode, then click "Load unpacked" and browse to the dist/chrome-web3-extension
subdirectory.
Now browse to https://w3url.w3eth.io/
and see what happens.
Things that could be improved about the extension:
- Allow the user to customize the RPC endpoint used by the extension.
- Get Chrome to add
web3://
to the list of URL schemes that it allows extensions to register as a handler for.ipfs://
is currently one of these schemes. As an IANA-registered URL scheme, it should be possible to addweb3://
to this list. - Implement automated internal retries. Currently, some requests randomly fail with "execution reverted" error messages. This could be handled in the
web3protocol
library as well. - Implement light client support, rather than contacting EVM RPC endpoints. This may be possible using Helios compiled to WebAssembly, which is supported in Chrome extensions.
- Add configuration to disable websites from using non-
web3://
URLs (external JavaScript).web3://
websites should not depend on web2 CDNs serving mutable JavaScript code. Pre-bake popular JavaScript into the extension so that they don't need to be loaded from the chain all the time. - Implement better caching mechanism. Right now the extension hard-codes a 30 minute caching buffer in order to improve page loading speed over the more correct
no-cache
setting. This may not be appropriate for all use-cases. The ERC-4804 spec needs a mechanism to specify the equivalent of the HTTPCache-Control
header. This could be done in a similar manner as ERC-7087. - Figure out whether it is possible to port the service worker into a sandbox context.
- Add support for injecting
window.ethereum
into thechrome-extension
page context. When the page attempts to use it, display some message telling the user to use WalletConnect instead.
ERC-4804 and the web3://
URL scheme was designed by Dr. Qi Zhou and EthStorage.
The heavy lifting of actually querying the EVM is done by the web3protocol
library written by nand2.
This extension is released under the MIT license.