Deposits
Deposit funds into the Root14 privacy pool. A deposit creates a private note and registers its commitment on-chain.
Three-Step Process
Step 1: Create a Note
A note is Root14's UTXO - a private balance record containing value, app_tag, owner, and a random nonce.
use r14_sdk::{Note, commitment, owner_hash, SecretKey};
use r14_sdk::wallet::{crypto_rng, hex_to_fr, fr_to_hex};
let mut rng = crypto_rng();
// Load owner from wallet
let owner_fr = hex_to_fr(&wallet.owner_hash)?;
// Create the note
let note = Note::new(1_000, 1, owner_fr, &mut rng);
// Compute the commitment: Poseidon(value, app_tag, owner, nonce)
let cm = commitment(¬e);
println!("commitment: {}", fr_to_hex(&cm));
Step 2: Save to Wallet
Push the note entry into the local wallet file for future spending.
use r14_sdk::wallet::{NoteEntry, load_wallet, save_wallet, fr_to_hex};
let mut wallet = load_wallet()?;
wallet.notes.push(NoteEntry {
value: note.value,
app_tag: note.app_tag,
owner: fr_to_hex(¬e.owner),
nonce: fr_to_hex(¬e.nonce),
commitment: fr_to_hex(&cm),
index: None, // populated after indexer sync
spent: false,
});
save_wallet(&wallet)?;
Step 3: Submit On-Chain
Compute the new Merkle root (including the new commitment) and invoke the deposit function on the r14 transfer contract.
use r14_sdk::merkle::compute_new_root;
use r14_sdk::soroban::invoke_contract;
use r14_sdk::wallet::fr_to_raw_hex;
// Strip the 0x prefix for Soroban
let cm_hex = fr_to_raw_hex(&cm);
// Fetch existing leaves from indexer, append cm, compute root
let new_root = compute_new_root(&wallet.indexer_url, &[cm]).await?;
// Submit to r14-transfer contract
let result = invoke_contract(
&wallet.transfer_contract_id,
"testnet",
&wallet.stellar_secret,
"deposit",
&[("cm", &cm_hex), ("new_root", &new_root)],
).await?;
println!("deposit tx: {}", result);
What Happens On-Chain
When deposit is called on r14 transfer:
- The commitment
cmis added to the contract's commitment set - The Merkle root is updated to
new_root - A deposit event is emitted with the commitment
- The indexer polls Soroban RPC events, picks up the new commitment, and inserts it into its local Merkle tree + SQLite database
After the indexer syncs, r14 balance will show the note's on-chain index.
Using the High-Level Client
use r14_sdk::{R14Client, R14Contracts};
use r14_sdk::wallet::{load_wallet, hex_to_fr, save_wallet};
let wallet = load_wallet()?;
let client = R14Client::from_wallet(&wallet)?;
let owner = hex_to_fr(&wallet.owner_hash)?;
let result = client.deposit(1_000, 1, &owner).await?;
// Save the returned note entry to wallet
let mut wallet = load_wallet()?;
wallet.notes.push(result.note_entry);
save_wallet(&wallet)?;
println!("commitment: {}", result.commitment);
println!("tx: {}", result.tx_result);
CLI
r14 deposit 1000
r14 deposit 1000 --app-tag 2
r14 deposit 1000 --local-only # skip on-chain, only create note locally