Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.privora.xyz/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers all FHE operations available in the Privora Program SDK.

Loading Encrypted Values

Before performing operations, load encrypted values from their references:
use privora_sdk_program::prelude::*;

// From hash reference stored in account
let price_ref: EncryptedRef<u8> = account.price_ref;

// Load the actual encrypted value
let price: Encrypted<u8> = price_ref.load()?;
The load() method:
  1. Takes the hash from EncryptedRef
  2. Fetches the ciphertext from the content store via syscall
  3. Returns an Encrypted<T> ready for computation

Arithmetic Operations

Addition

// Method syntax (returns Result)
let sum: Encrypted<u8> = a.add(&b)?;

// Operator syntax (panics on error)
let sum: Encrypted<u8> = &a + &b;
Operators use references (&a + &b) to avoid consuming the values.

Subtraction

// Method syntax
let difference: Encrypted<u8> = a.sub(&b)?;

// Operator syntax
let difference: Encrypted<u8> = &a - &b;
FHE subtraction can underflow. If a is less than b, the result wraps around (modular arithmetic). Design your program to avoid or handle this.

Multiplication

// Method syntax
let product: Encrypted<u8> = a.mul(&b)?;

// Operator syntax
let product: Encrypted<u8> = &a * &b;

Chaining Operations

// Calculate: (a + b) * c
let result = a.add(&b)?.mul(&c)?;

// With operators (use parentheses carefully)
let result = &(&a + &b) * &c;

// More readable with intermediate variables
let sum = &a + &b;
let result = &sum * &c;

Comparison Operations

Comparisons return EncryptedBool, which can be used for conditional logic.

Greater Than or Equal

let is_ge: EncryptedBool = buy_price.ge(&sell_price)?;

Greater Than

let is_gt: EncryptedBool = price.gt(&threshold)?;

Less Than or Equal

let is_le: EncryptedBool = amount.le(&limit)?;

Less Than

let is_lt: EncryptedBool = balance.lt(&required)?;

Equality

let is_equal: EncryptedBool = value_a.eq_enc(&value_b)?;
The method is eq_enc (not eq) to avoid conflict with Rust’s Eq trait.

Min/Max Operations

Get the minimum or maximum of two encrypted values:
// Minimum
let min_value: Encrypted<u8> = buy_qty.min(&sell_qty)?;

// Maximum
let max_value: Encrypted<u8> = price_a.max(&price_b)?;
These are implemented using encrypted comparison and selection.

Conditional Selection

The select method implements encrypted if-then-else:
let condition: EncryptedBool = buy_price.ge(&sell_price)?;

// If condition is true, return sell_price; else return buy_price
let fill_price: Encrypted<u8> = condition.select(&sell_price, &buy_price)?;
This is equivalent to:
if (buy_price >= sell_price) {
    fill_price = sell_price;
} else {
    fill_price = buy_price;
}
Key insight: Both branches are always computed. FHE cannot skip computation based on encrypted conditions.

Storing Results

After computation, store results back to the content store:
// Compute result
let result: Encrypted<u8> = a.add(&b)?;

// Store and get hash reference
let result_ref: EncryptedRef<u8> = result.store()?;

// Save reference to account
account.result_ref = result_ref;

Complete Example

use privora_sdk_program::prelude::*;

pub fn match_orders(
    buy_price_ref: EncryptedRef<u8>,
    buy_qty_ref: EncryptedRef<u8>,
    sell_price_ref: EncryptedRef<u8>,
    sell_qty_ref: EncryptedRef<u8>,
) -> Result<(EncryptedRef<u8>, EncryptedRef<u8>), ProgramError> {
    // 1. Load all encrypted values
    let buy_price = buy_price_ref.load()?;
    let buy_qty = buy_qty_ref.load()?;
    let sell_price = sell_price_ref.load()?;
    let sell_qty = sell_qty_ref.load()?;

    // 2. Check if orders can match (buy >= sell)
    let can_match: EncryptedBool = buy_price.ge(&sell_price)?;

    // 3. Calculate fill quantity (minimum of both)
    let fill_qty: Encrypted<u8> = buy_qty.min(&sell_qty)?;

    // 4. Use sell price as fill price (standard matching)
    // The fill price is the sell_price (taker price)

    // 5. Store results
    let fill_price_ref = sell_price.store()?;
    let fill_qty_ref = fill_qty.store()?;

    Ok((fill_price_ref, fill_qty_ref))
}

Operation Costs

FHE operations have different computational costs:
OperationRelative CostNotes
Addition1xFast, use freely
Subtraction1xSame as addition
Multiplication2-3xMore expensive
Comparison5-10xRequires bootstrapping
Min/Max5-10xBuilt on comparison
Select5-10xBuilt on comparison

Optimization Tips

  1. Batch comparisons: Do all arithmetic first, then comparisons
// Good: Arithmetic first, then compare
let total_a = price_a.mul(&qty_a)?;
let total_b = price_b.mul(&qty_b)?;
let a_is_larger = total_a.gt(&total_b)?;

// Avoid: Interleaving comparisons with arithmetic
  1. Minimize comparisons: Each comparison is expensive
// Good: One comparison
let can_match = buy_price.ge(&sell_price)?;

// Avoid: Redundant comparisons
let can_match = buy_price.ge(&sell_price)?;
let cant_match = buy_price.lt(&sell_price)?; // Redundant!
  1. Reuse loaded values
// Good: Load once, use multiple times
let price = price_ref.load()?;
let result1 = price.add(&amount1)?;
let result2 = price.add(&amount2)?;

// Avoid: Loading multiple times
let result1 = price_ref.load()?.add(&amount1)?;
let result2 = price_ref.load()?.add(&amount2)?; // Unnecessary second load

Type Safety

The SDK enforces type safety at compile time:
let a: Encrypted<u8> = a_ref.load()?;
let b: Encrypted<u64> = b_ref.load()?;

// This won't compile!
let sum = a.add(&b)?; // Error: expected Encrypted<u8>, found Encrypted<u64>

Error Handling

All operations return Result:
// Handle errors explicitly
let price = match price_ref.load() {
    Ok(p) => p,
    Err(e) => {
        msg!("Failed to load price: {:?}", e);
        return Err(e);
    }
};

// Or use ? operator
let price = price_ref.load()?;
let sum = price.add(&other)?;
let result_ref = sum.store()?;

Next Steps

Comparisons

Deep dive into comparisons and conditionals

Data Store

Low-level data store access