📝ArcInfer-Examples-Reference.md
Arcium Examples Reference - Complete 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
- Blackjack (Most Complex)
- Sealed Bid Auction
- Voting
- Coinflip
- Rock Paper Scissors (Against Player)
- Rock Paper Scissors (Against House)
- Ed25519 Signature
- Share Medical Records
- Key Patterns & Observations
1. Blackjack
Key Patterns & Observations
A. Encryption Ownership Model
Two key types control who can decrypt data:
| Type | Meaning | When to use |
|---|---|---|
Mxe | MXE network holds the key | Persistent server-side state (deck, game state, vote tallies) |
Shared | Client holds the decryption key | Data the client needs to read (player's hand, their vote) |
B. Input/Output Patterns
Inputs to #[instruction] functions:
| Pattern | Meaning |
|---|---|
Enc<Shared, T> | Client-encrypted data (bid, vote, move) |
Enc<Mxe, T> | MXE-encrypted state from on-chain account |
Mxe | MXE key handle - use to encrypt outputs for MXE storage |
Shared | Client key handle - use to encrypt outputs for a client |
u8, u64, etc. | Plaintext parameters (hand size, etc.) |
Outputs from #[instruction] functions:
| Pattern | Meaning |
|---|---|
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 |
| Tuples | Multiple 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 // moduloF. 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 keyG. Random Number Generation
ArcisRNG::bool() // Random boolean
ArcisRNG::shuffle(&mut arr) // Fisher-Yates shuffle of arrayH. 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 valueK. 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