main*
📝ArcInfer-Examples-Reference.md
📅February 15, 20265 min read📁Arcium

Arcium Examples Reference - Complete Source Code

#arcinfer#arcium#examples#reference#source-code

Arcium Examples Reference - Complete Source Code

Source: https://github.com/arcium-hq/examples

All examples use arcis = "0.8.0" and arcium-anchor = "0.8.0" / arcium-client = "0.8.0".


Table of Contents

  1. Blackjack (Most Complex)
  2. Sealed Bid Auction
  3. Voting
  4. Coinflip
  5. Rock Paper Scissors (Against Player)
  6. Rock Paper Scissors (Against House)
  7. Ed25519 Signature
  8. Share Medical Records
  9. Key Patterns & Observations

1. Blackjack

Key Patterns & Observations

A. Encryption Ownership Model

Two key types control who can decrypt data:

TypeMeaningWhen to use
MxeMXE network holds the keyPersistent server-side state (deck, game state, vote tallies)
SharedClient holds the decryption keyData the client needs to read (player's hand, their vote)

B. Input/Output Patterns

Inputs to #[instruction] functions:

PatternMeaning
Enc<Shared, T>Client-encrypted data (bid, vote, move)
Enc<Mxe, T>MXE-encrypted state from on-chain account
MxeMXE key handle - use to encrypt outputs for MXE storage
SharedClient key handle - use to encrypt outputs for a client
u8, u64, etc.Plaintext parameters (hand size, etc.)

Outputs from #[instruction] functions:

PatternMeaning
Enc<Mxe, T>Encrypted for MXE - stored on-chain
Enc<Shared, T>Encrypted for client - client can decrypt
T (with .reveal())Plaintext output - visible to everyone
TuplesMultiple outputs of mixed types

C. How .reveal() Works

// Reveal a primitive - makes it plaintext in the output
result.reveal()      // u8, bool, etc.
 
// Reveal a struct - all fields become plaintext
AuctionResult { ... }.reveal()
 
// Reveal a comparison
(value > 21).reveal()

D. How Arrays/Matrices Are Handled

Arrays of encrypted values are NOT directly supported as struct fields in the encrypted type system. Instead, arrays are packed into u128 values using base-64 encoding:

// Pack 52 cards (6 bits each) into three u128s
pub struct Deck {
    pub card_one: u128,   // cards 0-20 (21 cards * 6 bits = 126 bits)
    pub card_two: u128,   // cards 21-41
    pub card_three: u128, // cards 42-51
}
 
// Pack 11 cards into one u128
pub struct Hand {
    pub cards: u128,      // 11 cards * 6 bits = 66 bits
}

Boolean arrays ARE supported:

pub struct PatientData {
    pub allergies: [bool; 5],   // This works
}

E. How Multiplication Works

Multiplication on encrypted types works naturally using standard Rust operators:

// u128 multiplication in encrypted context
card_one += POWS_OF_SIXTY_FOUR[i] * array[i] as u128;
 
// These all work inside #[encrypted] blocks:
value += rank;         // addition
state.bid_count += 1;  // increment
card_one >>= 6;        // shift
card_one % 64          // modulo

F. Re-encrypting Data for Different Recipients

// Transfer data from one party to another:
// 1. Decrypt from sender's encryption
let input = input_ctxt.to_arcis();
// 2. Re-encrypt for receiver
receiver.from_arcis(input)
 
// Use .owner to re-encrypt for the same party:
player_hand_ctxt.owner.from_arcis(Hand::from_array(updated_hand))
// This preserves the original encryption key

G. Random Number Generation

ArcisRNG::bool()              // Random boolean
ArcisRNG::shuffle(&mut arr)   // Fisher-Yates shuffle of array

H. ArgBuilder Pattern (Anchor Program Side)

let args = ArgBuilder::new()
    // For Mxe params:
    .plaintext_u128(mxe_nonce)
 
    // For Shared params:
    .x25519_pubkey(client_pubkey)
    .plaintext_u128(client_nonce)
 
    // Pre-encrypted data from client:
    .encrypted_u128(encrypted_value)
    .encrypted_u64(encrypted_amount)
    .encrypted_u8(encrypted_choice)
    .encrypted_bool(encrypted_vote)
 
    // On-chain encrypted state (read from account):
    .plaintext_u128(state_nonce)
    .account(account_key, byte_offset, byte_size)
 
    // Plaintext parameters:
    .plaintext_u8(hand_size)
 
    .build();

I. Callback Output Destructuring

// Single encrypted output
Ok(InitAuctionStateOutput { field_0 }) => {
    // field_0.ciphertexts: [[u8; 32]; N]  (N = number of struct fields)
    // field_0.nonce: u128
}
 
// Tuple of mixed outputs
Ok(PlayerHitOutput { field_0: PlayerHitOutputStruct0 { field_0: hand, field_1: is_bust } }) => {
    // hand.ciphertexts, hand.nonce  (encrypted Shared output)
    // is_bust: bool                 (revealed plaintext)
}
 
// Struct fields for Enc<Shared, T> output:
// .encryption_key: [u8; 32]   (client's x25519 pubkey)
// .nonce: u128
// .ciphertexts: [[u8; 32]; N]

J. TypeScript Decryption Pattern

const cipher = new RescueCipher(sharedSecret);
const nonce = Uint8Array.from(event.clientNonce.toArray("le", 16));
const decrypted = cipher.decrypt([event.playerHand], nonce);
// decrypted[0] is a BigInt containing the packed value

K. Project Structure

example/
  Arcium.toml                    # Localnet config (nodes, backends)
  Cargo.toml                     # Workspace
  encrypted-ixs/
    Cargo.toml                   # arcis dependency only
    src/lib.rs                   # #[encrypted] circuit definitions
  programs/example/
    Cargo.toml                   # anchor-lang + arcium-* dependencies
    src/lib.rs                   # #[arcium_program] Anchor program
  tests/
    example.ts                   # TypeScript integration tests

L. On-Chain State for Encrypted Data

Each encrypted field maps to [u8; 32] on-chain. A struct with N fields becomes [[u8; 32]; N] on-chain:

// Arcis circuit struct (5 fields):
pub struct AuctionState { highest_bid: u64, highest_bidder_lo: u128, ... }
 
// On-chain Anchor account:
pub encrypted_state: [[u8; 32]; 5],  // 5 ciphertexts, one per field
pub state_nonce: u128,               // nonce for decryption