Skip to content

Deposit and Withdrawal ​

A Veilix payment is two transactions that do not share a signer. The first creates a note. The second spends it.

Deposit ​

You choose an asset and an amount in that asset’s smallest unit. The client creates a note:

text
vx2-<amountHex>-<privateKeyHex>-<blindingHex>[-<mint>]

The mint suffix is omitted for SOL. The note is the only record of the spending key. Save it before the transaction is sent, unless you are sealing it on-chain and accept that recovery depends on the same wallet.

The client then proves a deposit:

  • public amount in is the deposit amount
  • fee is zero
  • both inputs are empty notes
  • the first output is the new note
  • the recipient and relayer fields are the depositor, because nobody else is being paid

The depositor signs the transaction. SOL moves from the depositor to the SOL treasury. A token moves from the depositor’s token account to the treasury’s token account. The program appends the new commitment, records the empty second output, and stores the encrypted output bytes alongside the leaf.

The deposit is public in the ordinary sense: the sender, the asset, and the amount that entered the treasury are visible. What is not visible is the spending key inside the note.

A deposit above maxDepositAmount for that tree is rejected. Token-2022 mints that charge a transfer fee are not supported, because the amount that arrives would not match the note.

Withdrawal ​

A withdrawal spends one note.

  1. The client checks the recipient against the address-risk service.
  2. It finds the note’s leaf index and confirms the nullifier is unused.
  3. It chooses how much of the note to withdraw. Omit an amount to take the whole note.
  4. It loads the relayer fee and the on-chain platform-fee wallets, and computes the fee the program will require.
  5. It rebuilds the asset tree and proves the spend.
  6. It sends the proof to a relayer. The relayer signs and submits. The client polls until the transaction confirms or fails.

The recipient receives amount − fee. If you withdrew less than the note, the difference is a new change note. The change note is a new secret. If the wallet encryption key is loaded, that change note is sealed into the withdrawal’s encrypted output so a later scan can find it.

The relayer cannot substitute a different recipient or a different fee. Those values are bound into the proof. A malformed proof, an unknown Merkle root, a reused nullifier, or a fee that does not match the configured split is rejected on-chain.

Partial withdrawal ​

text
note of 10 SOL
    withdraw 2.5 SOL publicly
    change note of 7.5 SOL stays in the tree

The public withdrawal event shows 2.5 SOL, the recipient, the relayer, and the fee. It does not show the 10 SOL note, and it does not show the change note’s amount in the clear. The change commitment is just another leaf.

You can repeat this until the remaining note is too small to cover the fee.

Private Send is the same two steps ​

Private Send does not have its own instruction. For each recipient it creates a note for the exact amount, deposits it, waits until the indexer sees the leaf, proves a full withdrawal to that recipient, and queues the proof. The user signs the deposit. The relayer signs the withdrawal. The recipient does nothing.

Timing ​

The deposit is a normal Solana confirmation. The withdrawal waits on proof generation, relayer inclusion, and confirmation. A quote includes an etaSeconds estimate for the withdrawal leg. Keep the session open while a Private Send is proving and submitting. If the deposit landed and the withdrawal did not, the note is still spendable.

Legacy notes ​

Notes from the fixed-denomination pool do not use the vx2 prefix. The current program cannot spend them. A vx2 note is the only note this tree understands.