Example Primitives

Read Only Proofs

Read only proofs can either take a snapshot of the RAILGUN contract state at a particular point in time by saving the current Merkle root or allow proofs to be generated against the current contract state by checking if the proof is generated against a current or historical Merkle root.

SNARK validation will need to occur within your own smart contract, but you will pull the state data from the RAILGUN core contracts allowing you to prove arbitrary details over the RAILGUN state.


A basic proof of membership can be built on top of the RAILGUN account state with Read Only Proofs. A membership set can be constructed for eg. holders of an NFT within their RAILGUN private balance, or holders of a particular ERC20 token for NFT or token weighted voting.

The basic checks required for this construction are:

  • Proof is generated against a current or historical Merkle root (or the snapshotted Merkle root if snapshots are being used)

  • The UTXO is part of the Merkle Tree and the contents of the UTXO match your application parameters (eg. is an NFT from a certain collection or is a balance of a certain ERC20)

  • The global nullifier hasn't been seen by the RAILGUN contract

  • The application nullifier hasn't been seen by your application contract


Snapshots of the nullifier set can't be taken in the current architecture. Spending a UTXO after a snapshot is taken will result in not being able to generate a proof for that UTXO.

For this reason it is preferable to generate proofs against the current state instead of a snapshot where possible. For private NFT voting you can avoid using snapshots by using the NFT ID in the application nullifier to prevent double-voting instead, but in private ERC20 voting snapshots are unavoidable.

Conditional Transactions

Transactions can have additional validation rules attached to them by an external contract enabled by the adapt field. You will need to set 2 values on the transaction proof generated by a RAILGUN wallet to enable this: adaptContract and adaptParams. Setting the adaptContract field locks the transaction to only being valid if the address specified in adaptContract is msg.sender (see here). The adaptParams field is a 32 byte field bound to the SNARK proof but isn't validated by the core RAILGUN contract letting you implement your own validation rules, likely a hash of the contract.

Cookbook uses a multicall contract that allows arbitrary contract calls to be bound to RAILGUN transactions through this method. You can see how this is implemented here.

Atomic Swaps

Conditional payments can be used to implement an atomic swap protocol. You can include the commitment hash for a UTXO in the adaptParams field. Your atomic swap settlement contract will need to accept a bundle of 2 (or more) transactions and validate that the UTXO commitment hash for each transaction is paid into by another transaction in the submitted bundle.

Last updated