Noob question, whats different with Railgun?
Hi @zk-friendly
Railgun is a full, end-to-end product, while ERC-8086 and ERC-8085 are mainly about interface standards. They focus on what the primitives look like, not on prescribing a specific implementation.
If youâre asking how our reference implementation differs from Railgun, hereâs where you can dig deeper:
-
This is an early foundational privacy layer we designed for Ethereum â
https://github.com/ZK-Protocol/Native-Privacy-Asset-Protocol-Whitepaper
ERC-8086 originated from this work. -
This is the reference implementation for ERC-8086 â
https://github.com/0xRowan/erc-8086-reference
If youâre not very code-oriented, the easiest way is to try what weâve already deployed on testnet â anyone can experiment and verify things directly:
-
Anyone can create native privacy assets with a Zcash-style privacy model, as easily as creating an ERC-20 token:
https://testnative.zkprotocol.xyz/ -
Anyone can wrap ETH or any existing ERC-20 into a privacy asset and gain privacy features:
https://zerolayer.live/ -
Anyone can easily create dual-mode tokens (privacy + ERC-20 public mode), just like creating an ERC-20. Users can freely switch between private and public modes depending on their use case:
https://testdmt.zkprotocol.xyz/
ERC-8086, ERC-8085, and ERC-8091 are better understood as a modular privacy layer for Ethereum. Theyâre designed as building blocks â anyone can use them to build their own privacy products on top.
Your design is 100% same with railgun, shield and unshield, dual mode. but the utxo design is not good for UX
I intentionally tried out several different privacy solutions, and honestly the UX is way better than I expected.
The only downside right now is that you have to experience it on your own testnet. If wallets supported this protocol natively, it would be amazing to let users switch between privacy mode and public mode directly in the wallet with a simple toggle.
At that point, using privacy would feel just as easy as interacting with a regular ERC-20 token.
Iâd strongly suggest providing an SDK so wallets can integrate this more seamlessly.
Thanks for the suggestion â weâve been thinking along the same lines as well.
Iâm about as much of an outsider to this topic as one can be, but after reading through your proposal, would it make sense to reserve+standardize an address that âownsâ the private tokens? balanceOf(PRIVATE_ACCOUNT) would be equivalent to totalPrivacySupply().
Iâd also recommend specifying whether a Transfer event is emitted for toPrivacy and toPublic.
Also also, toPrivacy is weird English, IMHO. toPrivate sounds more natural.
Thanks for the thoughtful feedback!
Re: PRIVATE_ACCOUNT for private balances
We considered this approach but decided to stay with the burn/mint pattern for mode conversions:
-
Semantic clarity: When tokens enter privacy mode, they are cryptographically transformed into commitments - they donât âexistâ as ERC-20 balances anymore. Using burn/mint better reflects this transformation.
-
Supply tracking: We explicitly track
totalPrivacySupply()which is maintained through mode conversion operations. This provides clear visibility without needing a special account. -
Avoiding confusion: A PRIVATE_ACCOUNT that âholdsâ tokens but can never actually transfer them (except through ZK proofs) might be conceptually misleading.
Re: Transfer events for toPrivate/toPublic
Adopted. The spec now requires:
toPrivate(): MUST emitTransfer(account, address(0), amount)toPublic(): MUST emitTransfer(address(0), recipient, amount)
Re: toPrivacy â toPrivate
Thanks again for the review!
I would argue that because:
totalSupply()MUST return the sum of all public balances plustotalPrivacySupply()
then the private tokens are not burned.
Sorry, I may not have explained this clearly, and perhaps I didnât fully understand your question at first.
totalSupply() MUST return the sum of all public balances plus totalPrivacySupply().
Why is this necessary?
The core of token privacy lies in hiding holders, hiding balances, and hiding transfers. This kind of privacy is primarily about individual-level data and interactions â users need privacy at the personal level.
However, when it comes to totalSupply, we believe it should be intentionally designed to be public and accurate. totalSupply is a system-wide, collective metric, and it is highly valuable to all holders. Users should be able to clearly understand the overall state of the system: what the current total supply is, and how many tokens are in a privacy state.
This visibility helps prevent scenarios where tokens in the privacy state could be minted without bounds without users being aware.
The purpose of totalPrivacySupply() is to provide a public aggregate statistic for tokens that are currently in the privacy state, completing the system-level view described above. Tokens counted in this value are not burned; it represents an accurate and effective total amount of tokens currently held in privacy mode.
This value is not associated with any specific address. Instead, it represents tokens held collectively via commitments by many users. These holders are untraceable, forming a strong privacy environment where holders, balances, and transfers are all hidden.
You explained correctly, I think!
My argument is that if:
toPrivate/toPublicare analogous toburn/mint, and- minting/burning tokens modify the
totalSupply
Then:
toPrivate/toPublicshould decrease or increase thetotalSupplyrespectively.
Otherwise, youâre not being consistent and treating toPrivate/toPublic as halfway between a transfer and a mint/burn.
I donât think itâs any weirder than an external contract holding tokens. As far as observer is concerned, the tokens move in and out with Transfer events like any other account. permit-style interactions donât screw anything up, and theyâre essentially the same thing (except with ecrecover instead of a zk proof).
I actually find treating privacy operations as mint/burn to be more confusing. From an indexerâs perspective, how do you differentiate a mint/burn Transfer event from a privacy one?
On a completely separate note, how do dual-mode tokens fix this:
Splits liquidity between public and wrapped versions
?
Not a criticism, I just donât know how that works and am curious!