Operations

High-level overview of the operation flow

Within the Nocturne protocol, users carry out actions with their private funds through operations. Abstractly, an operation consists of two pieces of information: A list of JoinSplits and a list of actions.

An action consists of a contract address and some ABI-encoded calldata specifying a method call to make on that contract.

At a high level, an easy way to interpret an operation is:

  1. The list of JoinSplits specifies what assets to spend and how much of it to use for actions, all without revealing the owner of the assets

  2. The list of actions specifies what to do with said assets

Concretely operations contain other important fields for security, including:

  • A stealth address refundAddr that specifies an owner for any new notes created at the end of the operation (step 7 below)

  • Gas price / limit

  • Chain id / deadline to protect against replay attacks

Once submitted, operations are processed on-chain with the following procedure:

  1. The JoinSplit proofs are verified by the Teller against the committment tree.

  2. The old note nullifiers from the JoinSplits are checked against the nullifier set in the Handler to prevent double spends (more on this in the protocol overview).

  3. The old note nullifiers from the JoinSplits are added to the nullifier set.

  4. The new note commitments produced by the JoinSplits are queued for insertion into the commitment tree.

  5. Any publicSpend in the JoinSplits is sent from the Teller to the Handler (unwrapped).

  6. The actions are executed by the Handler using the unwrapped funds.

  7. Any new refund notes resulting from the actions are queued for insertion into the commitment tree.

  8. The underlying assets for the refund notes are sent back to the Teller contract.

To better understand what this means, let's consider some practical examples.

Confidential Payment

A "confidential payment" is a payment where the amount and recipient are hidden. We can represent a confidential payment via an operation with no actions and one or more JoinSplits where there is a publicSpend of 0 and the owner of one of the new notes is set to the payment recipient.

Since publicSpend equals 0, the underlying assets are never unwrapped and thus are never revealed, making the payment fully confidential. All that is seen on chain is that some unknown notes for unknown assets and amounts were spent and new unknown notes of that same unknown asset were created.

Anonymous ERC-20 Transfer

Suppose we have some USDC inside Nocturne and we wanted to unwrap it and pay an Ethereum address $100. In this interaction, the sender is anonymous but the recipient is not.

We can express this transfer via an operation with one or more JoinSplits for USDC, where the public JoinSplits' spends total to $100. The operation would have one action encoding a call to transfer $100 USDC to the recipient.

When submitted, this operation will unwrap the desired amount of USDC from Nocturne, then transfer it to the recipient.

Anonymous Uniswap Call

Suppose we have 3 WETH inside Nocturne and we wanted to swap 1 WETH for DAI. We can create an operation with one or more JoinSplits for WETH, where the JoinSplits' public spends total to 1 WETH. The operation would have one action encoding a call to Uniswap to swap 1 WETH for DAI.

When submitted, this operation will unwrap 1 WETH from Nocturne, call Uniswap to swap it for DAI, create a refund note for the resultant DAI, and send the DAI back to the Teller.

Last updated