Privacy in Ethereum — Stealth Addresses
Many thanks to Bill Liang, Amit Chaudhary, Moritz Boullenger and Ben DiFrancesco for advice and guidance.
Introduction
A major pain point for web3 crypto users is that of lack of privacy. The fact that all transactions are visible on a public ledger, and increasingly: associated with a single, legible ENS name, is a disincentive to users to perform certain activity, or causes them to perform those activities in ways that results in increased UX friction. One example is simply transferring funds from a hot wallet to a cold wallet or vice-versa. Users may not want to have one wallet linked to another, as they may not want their cold wallet balance to be seen. Currently, Ethereum addresses do not work like private bank accounts, because everyone can see your wallet, and increasingly, your social activity (SBTs, attestations, activities on various dapps etc.). It is for these reasons that Vitalik called out privacy as one of the three major technical transitions that Ethereum needs to undergo in order to be useful to average users.
Using existing privacy solutions, such as Tornado Cash, is a somewhat sub-optimal experience for several reasons. For one: users will rightfully be worried about having their address blacklisted on centralized exchanges or other platforms. Secondly, the user experience of interacting with services such as Tornado Cash is not very user friendly and really only suited to highly proficient users.
Stealth addresses provide a way to afford the user the privacy they take for granted with private bank accounts, and in a way that is easy to understand. Furthermore, innovations around stealth addresses mean that we can potentially do this in a way that remains compliant with AML regulations in numerous jurisdictions.
User Demand for Privacy
While research into the attitudes toward privacy among web and web3 users is not widely available, the below research was discovered via searches across the web, and the results broadly align, and demonstrate that there is a clear appetite for transaction privacy.
- A survey that was carried out in 2022 and published by Simin Ghesmati et al. in a paper entitled: User-perceived privacy in blockchain, stated that “half of the interviewees specified that transaction privacy is very important to them”. While this research was more related to Bitcoin than Ethereum, it is likely that there are similar attitudes to users of Ethereum as well. The sample size for this research was however, quite small (14 participants).
- Another interesting piece of research from 2022, published in Frontiers, entitled Political, economic, and governance attitudes of blockchain users, is more comprehensive, with 3,710 crypto users surveyed. The results showed that around a quarter of respondents indicated privacy is “the most important feature of blockchain and crypto”.
- In terms of attitudes to privacy in general, Consensys published research entitled Web3 and crypto global survey 2023, in which they surveyed 15,158 people online across 15 countries on various topics concerning the web, and not just crypto. The survey found that 83% respondents think that data privacy is important, while only 45% of respondents stated that they trust how the current Internet services use their data and personal information.
- A survey conducted by the UK’s Financial Services Compensation Scheme, published in April 2023 highlighted that 9% of respondents cited a “desire for anonymity/privacy” as their reason for investing in cryptocurrency.
Adoption of Stealth Transaction Protocols
Railgun has impressive numbers, with the usage of the protocol seems to be growing steadily over time as well, hitting a peak of over $70M TVL and $2 billion in volume as of November 2024.
Umbra has also seen a steady increase of people using their protocol (people registering a stealth address to their ENS), with almost 77K as of November 2024:
If we look at the most widely known (and now unfortunately infamous) privacy protocol in Ethereum, which is Tornado cash, we can see that it is still seeing significant use, despite the fact that the contract addresses are technically on OFAC’s SDN list.
The chart below shows the TVL of Tornado Cash over time. We can observe that the first major drop in TVL from its peak in around October 2021 coincided with a broader sell of in the crypto markets, with the second major drop off in August 2022 corresponding to OFAC placing Tornado Cash on the SDN list, and the third major drop corresponding to the OFAC redesignation in November 2022. Since then however, Tornado Cash has been seeing steady growth, despite the sanctions, reaching almost $600M in TVL. This is pretty strong evidence that there is a demand for basic transaction privacy in Ethereum.
The Stealth Address Landscape
This research has identified 4 main solutions for EVM chains in production as of today, those are:
Both Fluidkey and Umbra are based on Ethereum standards, which are:
Labyrinth and Railgun are based on the zerocash protocol (which zcash is also based on), which uses a shielded pool that users deposit funds into. Zerocash uses the concept of “notes”, which are basically cryptographic representations of value that enable private transactions. Each note includes a hidden value, owner key, and a unique number (a nullifier), with zk-SNARKs used to verify ownership without revealing details, and hence to transfer the value in the notes. When a note is spent, its nullifier is revealed to prevent double-spending, while new notes are created for the recipient(s), forming a UTXO system inside the shielded pool.
At a high level, stealth addresses work on the basic principle that a third party can send funds to an address that has never existed before, but which the intended recipient can find out about and can control (i.e. can subsequently spend those funds).
The erc-5564 standard specifies a mechanism by which a recipient can publish a stealth-meta-address, from which new Ethereum addresses can be derived. Anyone wishing to send funds to the recipient, can generate new addresses from the stealth-meta-address, and allow the recipient to find out about these funds without any direct communication ever taking place. All stealth address implementations work on this basic premise.
How Stealth Addresses Work
A stealth meta-address is essentially the concatenation of 2 compressed public keys, referred to as the “spending key” and the “viewing key”. The stealth meta-address uses the EIP-3770 chain-specific address format, with the addition of the “st:” prefix.. The following is an example of a stealth address:
st:eth:0x036ffa94a70a5b9608aca693e12da815fe0295f3739c7b22b0284c6d85c464ba4a02c0521b6fe31714b2ca0efa159402574355b754e0b50406b0b5fb33128eec3507
For simplicity, this stealth address can be associated with a normal Ethereum address (and therefore ENS), making it easier to send funds to the stealth address owner. To send funds, a sender would resolve the above address, and using the EIP-5564 standard, would create an ephemeral public key, from which they then derive the stealth address. The sender sends funds to the new stealth address, typically via a singleton contract that all stealth addresses recipients listen to for events. This contract emits an “Announcement” event that recipients subscribe to. Every time an announcement event is emitted, recipients will check the ephemeral public key in the announcement, combine it with their viewing private key, and determine if they have the ability to spend the funds sent to the stealth address. If they do, the wallet / client they are using will remember the stealth address and respective funds, adding it to the user’s displayed balance. To actually spend the funds, they can sign a transaction using the private spending key.
The following diagram outlines the end to end process hopefully a little more clearly:
Recall that this process happens entirely non-interactively, which means that the sender and recipient never have any direct communication, which means that there is effectively no link whatsoever between the sender and recipient that any third party can observe.
However, in order for this work in the first place, the recipient must make their stealth address known to the sender(s). One way to do this is to use the eip-6538 Stealth Meta-Address Registry. This is a singleton contract that allows users to register a stealth meta-address to a normal Ethereum address, which senders can then look up. This allows senders to resolve the normal address from ENS, and then look up the associated stealth meta-address from the registry.
This scheme breaks the link between the sender and the recipient, affording them both privacy from the entire world knowing their business. There are some caveats:
- When the recipient goes to spend the funds, whoever they transfer those funds to will see that they originated from the original sender (i.e. they can see the address the funds were transferred from and who previously sent funds to that address). What this means is that the chain of transfers remain intact and traceable, but they’re just not associated with the recipient in question (unless the recipient does something like send the funds to one of their known non-stealth addresses). Note that this only applies to erc-5564 implementations, not to Railgun or Labyrinth.
- One other side-effect of the above issue, is that in order to maintain optimal privacy, a user will likely need to leave funds in the stealth addresses they were originally sent to until they actually need them, rather than consolidating them together under a single address. This represents an overhead for remembering the addresses, and subsequently spending the funds at those addresses, as the amount you wish to transfer will need to be sourced from the combination of funds at various other addresses.
- In order to transfer funds from this address, the recipient will need to fund the address with some ETH in order to pay for gas, and this could potentially de-anonymise the recipient. This is a known issue with stealth addresses, and one reason why a number of implementations have implemented support for eip-4337 and a paymaster.
- One drawback of the stealth address scheme is that recipients need to monitor the blockchain for Announcement events and check every announcement to determine if they’ve been sent funds. This is obviously an impractical overhead for most users, especially when it comes to receiving funds on multiple networks. In order to make this process more efficient, the standard specifies a “view tag”, which is a truncated hash derived from the shared secret, and can be used to quickly discard transactions that are definitely not intended for them. Through using viewtags, the performance is not that bad on desktop, however it can be somewhat more noticeable on mobile devices. The only time the performance hit is really noticeable is if a wallet is being restored, in which case the wallet will need to scan through every address since the on-chain contract was deployed, which is time consuming.
- To work around this, users can optionally share the private viewing key with a trusted third party. This third party service can monitor various networks and can notify the users when they’ve received funds. This of course comes with a trade-off: although the third party can’t actually spend the users funds (they don’t have the private spending key to do so), they can see all the funds sent to a given recipient, which means the user needs to trust them with their privacy. Fluidkey does this by default.
- The standard stealth address protocol, i.e. ERC-5564, is designed to facilitate privacy-preserving transfers, however, non-financial use cases such as calling arbitrary smart contract functions require a bit more engineering, and are usually implementation specific.
Comparison Matrix
There are various ways that we can compare the four implementations of stealth addresses that this post explores. All implementations have subtle differences and trade-offs, but probably the most important points to call out are around traceability and obfuscation of value.
While both Fluidkey and Umbra allow funds to be transferred to a standard Ethereum address while breaking any link to the identity of the recipient, they still preserve the traceability of the transaction, meaning the sender is visible to anyone that examines the transaction history of the stealth address. This means that if you receive funds at a stealth address, the person you decide to send those funds to will see where they came from. Furthermore, the actual value being transferred is also visible. Railgun and Labyrinth hide the sender, as well as the value being sent, but with the trade-off that this all happens inside a single contract, and not as a normal transaction to a normal Ethereum address.
The figure below shows the how the protocols we look at in this post compare to each other across these two important dimensions of comparison.
The explore the differences in a little more detail, below is a comparison matrix of the four main stealth address protocols across six main dimensions:
- Complete end-to-end privacy (only sender and receiver sees payment)
- Forward-secrecy. Sending funds that were received via stealth transactions does not allow the second receiver to see where the funds originated from
- Follows the erc-5564 and erc-6538 standards
- Implements an extensible modular architecture that allows integration with third party dapps
- Does the implementation offer an SDK that developers can use to integrate?
- Does the solution provide a level of compliance through some sort of de-anonymization support?
- Does the design support the obfuscation of the amount / value being transferred?
| Protocol | E2E Privacy | Forward Secrecy | Open Standard | Modular Architecture | SDK | De-anonymisation support | Amounts hidden |
|-----------|-------------|-----------------|----------------|----------------------|------|--------------------------|----------------|
| Umbra | ✅ | ⛔️ | ✅ | ⛔️ | ⛔️ | ⛔️ | ⛔️ |
| Fluidkey | ⛔️ | ⛔️ | ✅ | ✅ | soon | ✅ | ⛔️ |
| Labyrinth | ✅ | ✅ | ⛔️ | ✅ | ✅ | ✅ | ✅ |
| Railgun | ✅ | ✅ | ⛔️ | ✅ | ✅ | voluntary | ✅ |
Some of the other nuances and differences are captured in more detail in the following section. Each implementation has interesting subtle differences that may or may not make a difference depending on your use case.
For example: in Fluidkey: all transfers go directly to stealth addresses on-chain, with Umbra: only ETH goes to stealth addresses on-chain, whereas tokens go to central contract, and with Railgun and Labyrinth, all transactions go to core contracts, not directly to stealth addresses on-chain.
Deep Dive on Stealth Address Implementations
Fluidkey
Fluidkey is an implementation of erc-5564 that allows users to send, receive, swap, and bridge ETH and erc-20 tokens. At the time of writing, Fluidkey is deployed on Base, Optimism, Arbitrum, Polygon, Gnosis, and Ethereum mainnet.
Users interact with Fluidkey through their web UI. When they log in first using their wallet, they sign a key generation message from which is derived their viewing and spending keys. These same keys are re-generated the same way every time the user enters the app.
There are a few ways that Fluidkey differs from other implementations. One way that Fluidkey differs from other implementations of stealth addresses is that users share their private viewing key with Fluidkey (actually a BIP-32 derived node of the key to be pedantic). This allows Fluidkey to generate stealth addresses for the user and also to notify the user when they’ve received payments to those addresses. It does mean however, that Fluidkey is in a position to view a user’s incoming transactions and balances, which is the trade-off. However, it still remains fully self-custodial.
Another interesting aspect of Fluidkey’s design is that it deploys a smart contract account to every new stealth address. This happens counterfactually only when funds from a stealth address are spent. The smart account is a 1/1 Safe account, and allows for things like gas sponsorship, making it easier to manage the various stealth addresses together. There are comprehensive details on this in their Technical Walkthrough.
Although Fluidkey does have the trade-off of retaining visibility into a user’s accounts, this can actually potentially be an advantage when it comes to compliance, though the exact framework for how Fluidkey will deal with things like potential law-enforcement requests in the future is not publicly available yet. They are based in Switzerland however, and while they have to comply with local laws, the data protection laws are very clear and very strong — there has to be a very clear case to share the data and would also have to be looked at by a court (see this post for an excellent overview of the privacy laws in Switzerland).
Users are also totally free to either export their transactions, or share their viewing keys with third parties (e.g. their accountant), which can be extremely useful, especially for businesses. It’s worth bearing in mind however, that with the erc-5564 spec, sharing the public key is “all or nothing”, meaning that it can’t reveal isolated individual transactions by themselves. Also, as with all implementations of erc-5564, traceability is not broken — just linkability to the user — which means that the transaction history of each stealth address is exposed to whoever has the viewing key. One not very well known feature of Fluidkey that helps to mitigate these trade-offs is the ability to rotate viewing keys, which would allow a user to use a new viewing key every month and only share view access to specific months with a third party.
One nice benefit of Fluidkey’s approach is that the stealth address itself is not generated by the sender, it’s generated by Fluidkey themselves pseudo randomly every time ENS is queried. This is much faster because users don’t have to scan through announcement events in order to identify transactions in which they are the recipient. This also means that the sender doesn’t need a stealth address wallet in order to generate a stealth address for the recipient — they just send funds to an address as they would to any other address and that’s it. This also means that there is no registry contract involved, which is unique to Fluidkey’s design, and a major advantage.
It’s worth saying that Fluidkey are committed to being entirely self-custodial, and they have open sourced their Stealth Account Kit library, and there are also several independently developed recovery interfaces available in the unlikely event that Fluidkey disappears overnight, meaning funds will never be locked-up or stuck.
Address Abstraction
By using smart contract accounts counterfactually, Fluidkey can automatically abstract away the management of the individual stealth addresses. This means that if you want to transfer a specific amount to a certain recipient from your balances across various stealth addresses, Fluidkey can automatically figure out which combination of addresses to use in order to source the funds for the transfer, handling all the gas costs and contract deployments, all behind the scenes. Fluidkey also allows users to exercise some control over which addresses are combined using a cool feature called labels, which allow the user to tag addresses into different categories.
ENS Resolution
Fluidkey requires users to create ENS names that are specific to Fluidkey. These static names take 2 forms: username.fkey.id and username.fkey.eth, one which is a URL to web interface to send someone funds, and the other a standard ENS name that can be used with wallets.
The ENS setup uses an ENS offchain resolver (aka erc-3668: CCIP Read) to return stealth addresses. Every time the off-chain resolver is queried, it generates and returns a new stealth address for the respective ENS name. This is a nice feature as it allows a user to have a single human-readable ENS name while still retaining the privacy of stealth addresses, as the generated stealth addresses can’t be linked to the ENS name retrospectively.
Cost
Fluidkey is free to use and does not charge a fee. There is the cost of deploying a Safe contract to every address that has funds when you want to spend those funds. However, while relatively expensive on mainnet, on L2s it’s really quite negligible, usually less than 1 cent, even when combining multiple stealth addresses into a single transfer.
They also can do gas sponsorship via the Safe deployments — they compute how much gas is going to cost, and take this from the users balance, even if it’s a token — in this case, a relayer deploys the safe and transfers the token on behalf of the user.
Umbra Cash
Umbra is an implementation of eip-5564 + eip6538 by Scopelift. When a user logs into the Umbra app, they go through a setup phase, in which they sign a message, from which is derived the spending and view keys and respective stealth meta-address. They then register this stealth meta-address to their main wallet address in an on-chain registry. This is where the implementation differs from Fluidkey.
Umbra’s implementation of erc-5564 is closest to the specification, in that they have no access to the user’s keys. While this means that Umbra (or anyone else) can’t see the users funds, this does mean that in order to be sent funds, the sender has to have an erc-5564 compatible wallet (or the Umbra app) in order to generate their stealth meta-address.
When someone wants to send funds to a user, they would normally use the Umbra app to do so. Essentially the Umbra app will look up the stealth meta-address registered to an ENS name / wallet address and generate a stealth address. Recipients can log in to the Umbra app and scan for any funds that were sent to stealth addresses belonging to them since the last time they logged in. Thanks to some clever caching, this seems to only take 10–15 seconds for a weekly scan, though users can also optionally specify a range of blocks to narrow the scan. Umbra v2 will include the use of viewtags, which will speed the process up even more.
Relayer
One issue with Stealth addresses that we’ve previously mentioned is that in order for the recipient to spend funds that were sent to a stealth address, that address will need to have ETH or other requisite gas token in order to pay transaction fees. On most networks, if the stealth address was sent ETH in the first place, this usually isn’t a problem. However, if the stealth address was sent an erc-20 token or NFT, then the act of funding the address with ETH to pay for gas can link the address to a user’s other addresses, removing their privacy.
In order to get around this, Umbra uses a construction involving a relayer. When any asset that isn’t ETH is sent to an Umbra user, it is actually sent to a special contract instead of directly to the stealth address. Users can spend funds sent to their stealth addresses by sending a meta-transaction (from the Umbra app) to Umbra’s relayer, which will transfer the funds out of the smart contract on the user’s behalf. The relayer will deduct some tokens in order to cover the costs of the gas fees, and only a certain number of tokens are supported initially.
Cost
The Umbra contract also applies a small fee when transferring funds on networks with low transaction fees, in order to disincentivize spam. The rationale is that spam would increase the cost of scanning through transactions to identify the relevant ones, and so this is seen as an acceptable trade-off.
Supported Networks
Umbra is currently deployed on Ethereum mainnet, as well as Optimism, Polygon, Gnosis Chain and Arbitrum.
The Umbra registry contract has an interesting design. The deployment method uses create2 and the standard create2 deployer, and the smart contract address is the same on any network. This means that if the contract exists on a given network, then the client can be sure that’s the right contract. The client can be configured to add networks, and anyone can deploy to any network. They have canonicalized the bytecode and the contract has no owner, which allows permissionless deployment of registry and announcement contracts on any chain by anyone.
Umbra v2
Scopelift is currently developing version 2 of Umbra, which introduces a new modular architecture that allows the core contracts to be extended to support new tokens standards or non-payment use cases. Using this new architecture, third party developers can build modules for any sort of token standard, e.g. erc-1155, erc-7621, support for erc-4337 paymasters, or anything else you can think of. Currently the Umbra core contract supports two scenarios, one for ETH, and one for erc-20. V2 will support many different scenarios.
Labyrinth
Labyrinth is a protocol that is not based on eip-5564 + eip6538, but instead uses zero knowledge proofs to add anonymity and privacy to transactions. Labyrinth’s whitepaper describes it as a “zkFi” middleware: “zkFi offers a packaged solution that acts as a privacy middleware with built-in compliance“. Built-in compliance refers to Labyrinth’s “Selective De-Anonymization”, which is a sophisticated solution that allows for certain transactions to be de-anonymised to specific authorized actors, i.e. legal authorities like interpol etc. while maintaining transparency and openness.
The core smart contracts that Labyrinth uses include a multi-transactional and multi-asset pool which allow the user to transact multiple assets in a single transaction. In order to spend assets, a user scans the network and fetches encrypted notes data, decrypts the notes, and filters for the assets it wants to spend. Subsequently, the user creates a ZKP that includes the transactions and a signature from the signing key associated with the notes it wants to spend transactions from.
Part of Labrynth’s core contracts include a converter contract, which interfaces with modular proxy contracts, that are basically proxies to external contracts. So for example: if a user wants to use Labyrinth to interact with Uniswap, the user would craft a transaction that would use the converter contract to call a swap operation on a Uniswap pool via Uniswap’s proxy contract.
Labyrinth’s zkFi protocol uses “notes” to track balances and transfers. A note is essentially a data structure that describes some amount of some asset and which address it belongs to. A client stores the information needed to recreate a note, and uses this to spend assets. A commitment to the note, (a hash of the asset id, owner, and value) is stored in a merkle tree on-chain. In fact, Labyrinth uses two merkle trees, one for notes, and one for root addresses.
The note data structure contains the following:
assetId
: Identifier for the asset (ETH, WBTC, MATIC, etc.) this note represents the value of.value
: Value or amount the note represents.leafIndex
: The leaf index of the commitment merkle tree where this note will be inserted.blinding
: A random blinding factor.rootAddress
: The root address of a user having the authority to spend it.revoker
: The chosen revoker’s public key point.
You’ll notice that the data structure above does not contain any reference to the owner of the assets, which is odd, as the commitment that is recorded in the notes merkle tree is a hash of asset id, value, and owner. In fact the owner is calculated from the root address, the revoker, and a random blinding factor, and so to external observers, the owner is in fact a newly created address for every new transaction.
Shielded Pool
What’s really interesting about Labyrinth, which is also slightly different from vanilla stealth address based protocols, is that the asset pool is in fact a shielded pool, which uses the concept of notes to create a sort of shielded UTXO pool which affords forward-secrecy to transactions. Recall that with eip-5564 implementations, the entity to whom a user transfers funds will be able to see where those funds came from. In other words, Alice pays Bob using a stealth address, Bob pays Charlie, so now Charlie can see that Bob initially received the funds from Alice, and so on. This is not the case with Labyrinth’s shielded pool.
To understand how this shielded pool works, we need to look at how funds are transferred inside the protocol:
A user’s balances in the shielded pool are the sum of the notes of the respective assets. In order to spend those notes, the user needs to reveal the “nullifiers” of those notes. A nullifier is unique to a note, and once that note is spent, the nullifier is marked to prevent double spends, and a new note is created from that spent note. Several notes of the same asset can be combined, and several new notes can be created. The nullifier is simply the hash of (𝑙,𝑐,𝛿), where 𝑙 is the index of the note commitment in the notes merkle tree, and 𝑐 is the commitment, and 𝛿 is a random element also referred to as a blinding factor.
A recipient of a stealth transaction transfer identifies the transfer through the same means as eip-5564, insofar as they listen to events emitted from the core contracts, and determine which stealth addresses they will be able to send funds from, recording them locally. Incoming funds identification is also made faster by utilizing view tags and by local caching and syncing of notes asynchronously during the app’s lifetime.
There is ongoing research into making the process of discovering received funds faster, take this proposal from Aztec for example: Request for Proposals: Note Discovery Protocol — Aztec.
For spending the funds, the user also has to generate a zk-proof, unlike erc-6654 implementations, which are basically normal Ethereum addresses. Generating a proof requires a compatible wallet, with the performance being relatively good, taking about 20 seconds or so on a mid-range Android device.
Bundler and Converter
There are a couple of nice features that Labyrinth offers which solve some pain points of stealth transactions. Transactions sent to the Labyrinth core contracts are sent as user operations via erc-4337 bundlers. This set up allows for spending notes without ETH or requiring gas tokens for the transactions, as the user can leverage the erc-4337 paymaster to pay gas on their behalf, which adds a further layer of privacy. The only exception is the initial deposit which is not submitted as a userop. This use of er-4337 paymaster also has the added benefit of being able to pay for gas via the assets being transferred, even if they are erc-20 tokens, and for this Labyrinth exposes a gas price oracle API.
Another very nice feature that Labyrinth has is a modular architecture that allows for converter contracts to act as proxy to third party dapps. This allows users to not just transfer funds using stealth transactions, but also to interact with third party dapps such as DEXs, (e.g. Uniswap, Aave, Lido etc.). These proxy “converter” contracts essentially implement a function which takes in which some amount of an asset, and some amount of asset comes out, with the underlying logic residing in some third party contract.
Compliance Solution
Labyrinth ensures compliance and regulatory adherence through a framework called Selective De-Anonymization (SeDe).
Recall that the data structure of a note contains a field called “revoker”. The revoker is the address of a specific entity that can initiate the process of de-anonymization. A user must choose at least one revoker from a predefined list. The revoker isn’t solely responsible for identifying potentially illicit or illegal activity, but can respond to requests from law enforcement agencies.
Revokers don’t have the unilateral capacity to de-anonymize transactions directly, but they are responsible for initiating requests for de-anonymization. These requests are posted publicly to the guardians, which are a committee of entities that oversee privacy and compliance. Guardians must respond to the request by voting on whether or not to grant permission to de-anonymize the transaction(s). If the guardians can reach a quorum and vote in favor, then the revoker can decrypt transaction data, allowing them to link the transaction in question to the previous transactions until the transaction chain has been fully de-anonymized.
This system creates a series of checks and balances, insofar as the guardians can’t unilaterally decide to reveal transaction data, and even if they collude, they can’t do anything without the revoker, and the revoker alone can’t do anything with a majority vote of the guardians.
Railgun
RAILGUN is a stealth transaction privacy system deployed on Ethereum, BSC, Polygon, and Arbitrum. The protocol is similar in some ways to Labyrinth insofar as it is based on notes, which are stored in a merkle tree as commitments, and form a UTXO set, i.e. the notes can be spent by creating new notes for other recipients. In order to spend a note, a nullifier is added to the state for that note, preventing double spends. Only the owner of a note can calculate its nullifier, which is based on the hash of the spending key and the index of the notes on the merkle tree, meaning only the owner of the note can spend it (and can only spend it once).
Stealth meta-addresses in Railgun use the prefix of “0zk”, and like eip-5564, is a concatenation of the public viewing key and public spending key. Railgun however uses Ed25519 keys on the BabyJubJub curve instead of ECDSA & secp256k1. In the same manner as eip-5564, users scan over all emitted events from the Railgun contracts and use their viewing key to determine which events represent transfers to their wallet.
Railgun uses a network of broadcasters, which are essentially relayers who accept meta-transactions from users and broadcast the actual transaction to the respective blockchain, paying the gas fees on behalf of the user. Direct interactions with the Railgun contracts come from this network of broadcasters, protecting the end users anonymity. Transactions that are sent from users to broadcasters are encrypted to a specific broadcaster’s public key and communicated using the Waku protocol.
Railgun has a modular architecture which allows it to interact with external smart contracts, allowing for functionality beyond simple transfers. It does this via the AdaptRelay contract, which shields and unshields the tokens before and after interacting with an external contract, e.g. unshield token A, swap for token B on some AMM, shield token B back to the original owner.
With version 3, Railgun is planning to leverage eip-4337 as well as supporting legacy meta-transactions. They are hoping that by maintaining a dedicated eip-4337 userop mempool for Railgun, that independent solvers can participate as broadcasters. They are currently collaborating with Umbra on researching this, and identifying edge cases and how to address them, see the section Railgun v3 below for more details.
Fees
The Railgun protocol takes a fee of 0.25% for deposits and withdrawals. These fees are sent to the DAO treasury, and these fees are paid out over time to stakers of the RAIL governance token. As well as the 0.25% deposit and withdrawal fee, broadcasters generally apply their own fee as well, and these usually amount to approximately 10% of whatever the gas fee is for the actual on-chain transaction.
Governance
Railgun has a governance system which allows for any form of proposal to be submitted, and no changes to any of the core contracts (including treasury & governance contracts) can happen without DAO proposal. Unusually, separate instances of Railgun have their own governance. For example, Railgun on Ethereum, Polygon and Binance Chain have their own separate governance systems and tokens.
SDK and Cookbook
Railgun offers a comprehensive and well documented SDK that wallet or dapp developers can use to build in stealth address functionality via support for Railgun. Railgun also has a community maintained cookbook with “recipes” that allow dapp developers to provide modules for Railgun that allows users to interact with their dapp using Railgun. For example, a developer can write a recipe for a DEX that allows users with token balances in Railgun to swap tokens privately.
RailGun v3
Railgun’s next iteration will make transactions about 50% — 60% cheaper. The other changes in version 3 are a move to supporting eip-4337 userops via a dedicated mempool. Also, v3 will provide support for an intents based architecture, that will allow solvers to compete for best execution, though the details are very high level at the time of writing. They are currently working with CowSwap on this and plan on using pre and post hooks to allow solvers to access funds.
Railgun Connect
Arguably the most interesting change that is proposed is something called Railgun Connect, which is described as a similar tool to WalletConnect, in that it enables 0zk addresses to connect to most frontends for private use, without requiring those dapps to specifically provide explicit integration with Railgun via custom modules.
Railgun Connect is a browser extension, and what it does is actually fork the state of the chain locally using HardHat under the hood, and injects its own web3 provider into the dapp, with an RPC endpoint of the local HardHat version of the chain. This then allows you to carry out the interactions with the dapps contracts as you normally would, records the transactions, then batches them up and creates a snark proof, and sends this to the actual Railgun contracts on the real chain. While this description is slightly over simplistic, it conveys the general idea. What this does is allows you to basically interact with virtually any dapp (with some edge cases for dapps that do extra off-chain processing). The caveat is that saving the state of the chain locally is a heavy operation, but once done, it means you can interact with dapps using Railgun’s stealth addresses without seeing any difference from using a normal wallet.
Conclusion
There are some interesting proposals for enshrining stealth addresses in the Ethereum protocol. For example, Inco uses the idea of an erc-20 “wrapper”, which wraps a normal erc-20 contract, and encrypts all balances. Transfers and approvals are all carried out on the encrypted state via fully homomorphic encryption. Inco relies on precompiles which currently only exist on its own network, but may some day make it to Ethereum.
There is also another proposal called EIP-7503: Zero-Knowledge Wormholes which is based on a design called “proof-of-burn”, though this doesn’t seem to be getting a lot traction, probably because it requires an update to the EVM, without which it can really only be implemented at the token level, using an erc-20 design that supports eip-7503 specifically (or if an L2 decides to add supports to their EVM opcodes).
Aztec is perhaps the most sophisticated privacy technology out there, but it does require users to bridge funds to Aztec in order to use it, which may or may not be an unacceptable UX for most users. However, if the demand for basic transaction privacy grows among Ethereum users, then Aztec could have a unique value proposition that makes it a very valuable L2 as dapps and users migrate to a platform that affords them privacy by default.
Similarly, Intmax is an Ethereum L2 (based on a Plasma design) that is privacy focussed, and also has a regulatory compliant aspect by verifying the legitimacy of individual funds through ZKP-based AML proofs and imposing limits on transaction amounts. Intmax is limited in terms of providing privacy for transfers while EVM smart contract operations are not private. However, unlike Aztec, smart contracts can be written in Solidity, which some developers may prefer (depending on the use case).
Wallet Support
While we are seeing increased adoption of stealth address protocols, which is promising, there remains a number of challenges. The most important challenge is that they are not fully supported in mainstream Ethereum wallets (not yet anyway). Mainstream wallets probably have several choices to make if they are going to provide support for stealth addresses. Some of these choices include:
- Will they provide opinionated support for a single implementation, or will they build and maintain a some sort of comprehensive aggregator across multiple protocols? The latter is likely to be prohibitively expensive in terms of development and maintenance.
- Will there be regulatory considerations, and will they need to take a position on the extent and mechanism for selective de-anonymisation (i.e. like Labyrinth’s)?
- Stealth addresses require an on-chain component vis-a-vis the registry contract (except Fluidkey), so this means every EVM network has to be supported explicitly by the wallet (though Umbra’s design facilitates permissionless deployment of the registry).
- Stealth addresses make existing integrations with block explorers (e.g. Etherscan) more complicated. For example, the “View on Explorer” button will not work for a stealth meta-address, where the wallet shows the aggregate balance. This issue may exist for transfers as well. The various edge cases such as this will need to be thought about.
- Depending on the underlying implementation, a user will only be able to use the stealth address effectively with a specific set of dapps, namely those that are supported by the underlying protocol. This will be possible with modular stealth address architectures. However, not all dapps will be supported, and this will need to be communicated to the user somehow. It is somewhat easier with eip-5506, but it still has its complications and edge cases, and this may often leak through to the wallet UX.
There is also scope for improvements in preventing user’s poor privacy hygiene, see this paper: “Anonymity Analysis of the Umbra Stealth Address Scheme on Ethereum” for how the authors successfully de-anonymized 48.5% of stealth transactions on Ethereum mainnet. The heuristics they used to do this had nothing to do with the protocols, and more around privacy hygiene, e.g. a user sends funds into a stealth address they control, and then sends those funds back to the address they originally sent them from, mistakenly thinking that the traceability is broken. Overall, the authors identified 6 different heuristics that can be used to de-anonymize stealth address transactions, mostly all based around not following best practice. However, these are critical UX issues that need to be addressed.
Overall, I’m pretty bullish on stealth addresses, and privacy in Ethereum in general. I think we’ve made pretty impressive progress, and discovered some challenges that are very fixable. I’m confident that mainstream wallets will figure out how to provide the level of support for stealth addresses that allow users to use them with minimal friction and thought, affording the normal level of privacy that users expect and deserve.
Massive shout out to all the teams that have dedicated time and hard work into researching and building stealth address infra, including the four protocols I’ve talked about in this post. Their hard work and tenacity will make a huge difference to Ethereum!