UX: Private Transactions

Proof Generation and Relayers are crucial pieces of the private transaction process. These steps each require complex interactions, and the user experience (UX) design is critical to ensure that private transactions are simplified for users.

Order of Operations

There is a typical order of operations to every private transaction. The first two actions (#1 gas estimate and #2 relayer fee calculation) occur automatically. They should refresh any time a transaction's parameters change.
#3 Generate Proof should occur on Proof Generation action by the user, typically through a Button. It's recommended to show a loading indicator through the proof generation process. Every proof generation
#4 Populate and #5 Submit should occur on user submission.
It is recommended as a two-part process, as proof generation can take up to 30 seconds depending on device and complexity of the transaction.
Please note that each step can throw errors, so it's best practice to wrap calls in a try/catch in order to report the error message to the user.

1. Gas Estimation

First, we estimate the gas for a transaction.
Gas Estimates must be re-estimated when any of the gas-estimate parameters change. Each parameter can impact the number of UTXO inputs and outputs, thereby changing the amount of gas to execute the transaction.

2. Relayer Fee Calculation

Next, the gas estimate is used to calculate the fee that is paid to a Relayer. Relayers typically charge small premiums on top of gas, and they accept a variety of ERC-20 tokens as payment.
Pro tip: The @railgun-community/waku-relayer-client repo automatically listens for fee broadcasts and determines the optimal Relayer for a given ERC-20 fee token.
Once a Relayer and Fee Token is selected, calculate the Relayer Fee using calculateRelayerFeeTokenAmount.

3. Generate Proof for Transaction

This uses the Groth16 Prover to build a zero-knowledge proof of a user's private transaction. Automatically downloads circuit assets if necessary.
The challenge here is for the user experience – a Relayer Fee expires after a certain period (usually around 3 minutes). Each Relayer broadcasts its current fees and expirations. If the fee expires, the transaction will need to be proved again to be valid.

4. Populate Proved Transaction

This is where we generate the actual encrypted transaction.
The "populate" method for each private transaction will validate the last generated proof and wrap it into a transaction. It then generates an encrypted PopulatedTransaction that is serialized for easy submission through a Relayer.

5. Submit Transaction

Finally, submit the transaction through a Relayer using method from @railgun-community/relayer-client, transactWithRelayer.

Self-Signing Transactions

It is possible to use an owned public wallet to self-sign a transaction, rather than using a Relayer. While the contents of the transaction will be encrypted, the transaction itself will be associated with the self-signing public wallet on the blockchain.
In the self-signing flow, we can skip the #2 Relayer Fee calculation entirely, and sign/send the serialized transaction to the blockchain like any other public transaction.
In order to retain anonymity, users can seed the public wallet with gas using a Base Token Unshield. As long as the public wallet is only used to sign private transactions, this achieves the same level of anonymity of using a Relayer.