Search
⌃K

Transaction Parameters

High-Level Transaction Parameters:
  1. 1.
    Verifier
  2. 2.
    Verifier App
  3. 3.
    Input Utxos
  4. 4.
    Output Utxos
  5. 5.
    Relayer
  6. 6.
    Signer
  7. 7.
    Accounts
    1. 1.
      Sender Spl
    2. 2.
      Recipient Spl
    3. 3.
      Sender Sol
    4. 4.
      Recipient Sol
Verifier Selection:
First, we need to select the verifier we want to use. See System Verifier for a list.
We would probably select verifier_zero for a normal shield because it is fast and cheap (it only takes one transaction).
If we have a lot smaller UTXOs, we would want to select verifier_one, which allows for ten input UTXOs.
For app UTXOs, we would need to select verifier two, which is the only one that enables application UTXOs.

Shield Utxo Selection

We want to shield 1 sol and 10 USDC.
Select Relayer: We do not need a relayer for a deposit
Select Utxos:
Let's assume we do not have any input UTXOs we want to merge yet, and for performance, we select verifier_zero, which takes two inputs and two outputs. Therefore, we set all input UTXOs to 0, meaning all amounts to 0.
Because we do not have any input UTXOs to pass the sum check, the public amount must be the same as the deposit UTXO. The compilation step in the Transaction class defines public inputs such as the public amount.
In a zero-knowledge proof circuit, every input must be set regardless of whether it is checked. Therefore we need to pass in dummy values. For UTXOs where amounts = 0, the circuit does not check the Merkle proof.
Input Utxos:
Parameter
Utxo0
Utxo1
  1. 1.
    Amount Sol
0
0
  1. 2.
    Amount Spl
0
0
Amounts for input UTXOs used to fill inputs up for the circuit need to be 0. The rest of the UTXO can be random since the UTXO is not checked.
Output Utxos:
Parameter
Utxo0
Utxo1
  1. 1.
    Amount Sol
1_000_000_000 lamports
0
  1. 2.
    Amount Spl
10
0
  1. 3.
    Asset Spl
USDC
0
  1. 4.
    blinding
Poseidon(31 random Bytes, shielded private Key)
31 random Bytes
  1. 5.
    Shielded Pubkey
User PublicKey
Random
  1. 6.
    instructionHash
0
0
  1. 7.
    Pool Type
0
0
  1. 8.
    Version
0
0
*4. By hashing the random bytes, which add randomness to the commitment hash with the shielded private key, we can distinguish between our own UTXOs and UTXOs others sent to us. Others will not know our private key; therefore, they can only send us transactions with random blinding.
Public Inputs:
public Amount Spl = 10
SumOutput Spl = SumInputSpl + public amount Spl;
public Amount Sol = 1_000_000_000
SumOutput Sol = SumInputSol + public amount Sol;

Transfer Utxo Selection

Select Relayer: We need a relayer for an anonymous transfer.
Input Utxos:
Parameter
Utxo0
Utxo1
  1. 1.
    Amount Sol
1_000_000_000 lamports
0
  1. 2.
    Amount Spl
10
0
  1. 3.
    Asset Spl
USDC
0
  1. 4.
    blinding
Poseidon(31 random Bytes, shielded private Key)
31 random Bytes
  1. 5.
    Shielded Pubkey
User PublicKey
Random
  1. 6.
    instructionHash
0
0
  1. 7.
    Pool Type
0
0
  1. 8.
    Version
0
0
Output Utxos:
Parameter
Utxo0
Utxo1
  1. 1.
    Amount Sol
999_900_000 lamports
0
  1. 2.
    Amount Spl
0
10
  1. 3.
    Asset Spl
0
USDC
  1. 4.
    blinding
Poseidon(31 random Bytes, shielded private Key)
31 random Bytes
  1. 5.
    Shielded Pubkey
User Public Key
Recipient Public Key
  1. 6.
    instructionHash
0
0
  1. 7.
    Pool Type
0
0
  1. 8.
    Version
0
0
Public Inputs:
public Amount Spl = 0
SumOutput Spl = SumInputSpl + public amount Spl;
public Amount Sol = 21888242871839275222246405745257275088548364400416034343698204186575808395617
SumOutput Sol = SumInputSol + public amount Sol;
A transfer is essentially a withdrawal of the relayer fee to the relayer. In this example, we use a relayer fee of 100_000 lamports. To keep the sum of input and output UTXO amounts consistent, we must subtract the amount we want to withdraw. However, the circuit does not allow for negative numbers, and we need the additional operation to make deposits. The arithmetic inside the circuit is modulo the field size of the finite field the circuit is defined over. We can use this property to subtract by adding this very high number you see as the public amount sol above by creating a number larger than the field size. When creating a number larger than the field size, the modulo is triggered, reducing our value to the amount sol minus the relayer fee.
Computation of Public Sol Amount:
SumOutputUtxosSol = 999_900_000
SumInputUtxosSol = 1_000_000_000
bn254ScalarFieldSize = 21888242871839275222246405745257275088548364400416034343698204186575808495617
public Amount Sol = SumOutputUtxosSol - SumInputUtxosSol + bn254ScalarFieldSize mod bn254ScalarFieldSize
SumOutputUtxosSol = SumOutputSol + public amount Sol;
const anchor = require("@coral-xyz/anchor");
const bn254ScalarFieldSize = new anchor.BN("21888242871839275222246405745257275088548364400416034343698204186575808495617")
const relayerFee = new anchor.BN(100_000)
const sumInputAmount = new anchor.BN(1_000_000_000)
const sumOutputAmount = sumInputAmount.sub(relayerFee)
const publicAmountSol = sumOutputAmount.sub(sumInputAmount).add(bn254ScalarFieldSize).mod(bn254ScalarFieldSize);
console.log(sumOutputAmount.toString() === sumInputAmount.add(publicAmountSol).mod(bn254ScalarFieldSize).toString());

Unshield Utxo Selection

Select Relayer: We need a relayer for truly private transfers and unshields.
Input Utxos:
Parameter
Utxo0
Utxo1
  1. 1.
    Amount Sol
999_900_000 lamports
0
  1. 2.
    Amount Spl
0
0
  1. 3.
    Asset Spl
0
0
  1. 4.
    blinding
Poseidon(31 random Bytes, shielded private Key)
31 random Bytes
  1. 5.
    Shielded Pubkey
User Public Key
Random
  1. 6.
    instructionHash
0
0
  1. 7.
    Pool Type
0
0
  1. 8.
    Version
0
0
Output Utxos:
Parameter
Utxo0
Utxo1
  1. 1.
    Amount Sol
0
0
  1. 2.
    Amount Spl
0
0
  1. 3.
    Asset Spl
0
0
  1. 4.
    blinding
31 random Bytes
31 random Bytes
  1. 5.
    Shielded Pubkey
Random
Random
  1. 6.
    instructionHash
0
0
  1. 7.
    Pool Type
0
0
  1. 8.
    Version
0
0
Public Amounts
public Amount Spl = 0
SumOutput Spl = SumInputSpl + public amount Spl;
public Amount Sol = 21888242871839275222246405745257275088548364400416034343698204186575808395617
SumOutput Sol = SumInputSol + public amount Sol;
Computation of Public Sol Amount:
SumOutputUtxosSol = 0
SumInputUtxosSol = 999_900_000
bn254ScalarFieldSize = 21888242871839275222246405745257275088548364400416034343698204186575808495617
public Amount Sol = SumOutputUtxosSol - SumInputUtxosSol + bn254ScalarFieldSize mod bn254ScalarFieldSize
SumOutputUtxosSol = SumOutputSol + public amount Sol;
const anchor = require("@coral-xyz/anchor");
const bn254ScalarFieldSize = new anchor.BN("21888242871839275222246405745257275088548364400416034343698204186575808495617")
const relayerFee = new anchor.BN(100_000)
const sumInputAmount = new anchor.BN(999_900_000)
const sumOutputAmount = sumInputAmount.sub(relayerFee)
const publicAmountSol = sumOutputAmount.sub(sumInputAmount).add(bn254ScalarFieldSize).mod(bn254ScalarFieldSize)
console.log(sumOutputAmount.toString() === sumInputAmount.add(publicAmountSol).mod(bn254ScalarFieldSize).toString());

Passed-in Accounts:

Name
Shield
Transaction/Unshield
Note
Verifier State
pda/not used
pda/not used
For verifiers which need two transactions because of the amount of data being sent. Is created in the first transaction and closed after the second.
sender spl
user
merkle tree pda
recipient spl
merkle tree pda
user
sender sol
user
merkle tree pda
recipient sol
merkle tree pda
user
relayer fee recipient
not used
relayers choice
The recipient of the relayer fee.
escrow
ecrow pda
not used
is created and closed in the same transaction to deposit sol to the merkle tree without an extra transfer instruction
token Authority
pda
pda
Leaves Pdas
pda
pda
Number depends on the verifier
Nullifier Pdas
pda
pda
Last modified 20d ago