Quick Start
Get started with the Arcium TypeScript SDK for confidential computation on Solana
Introduction
Arcium is a decentralized confidential computing network that runs Multi-Party Computation (MPC) over confidential data on Solana. Unlike traditional computation, which must expose data to process it, Arcium computes over fully confidential data, so it stays confidential end to end.
The SDK provides client-side libraries for interacting with confidential Solana programs and the Arcium network, and works with Anchor and Solana's existing tooling.
Use it to establish encryption keys, encrypt computation inputs, submit them to the network, and decrypt the results — without exposing your data to any single party.
Installation
Install the Arcium client SDK for encryption, PDA helpers, and computation management:
npm install @arcium-hq/clientOverview
This guide will walk you through the complete flow of using the Arcium TypeScript SDK to submit and process confidential computations:
- Setup: Configure your Anchor provider and Arcium cluster connection
- Encrypt: Establish encryption keys and encrypt your computation inputs
- Submit: Send confidential computations to the Arcium network
- Monitor: Track computation finalization on-chain
- Decrypt: Retrieve and decrypt computation results
Prerequisites
Complete the Arcium Installation Guide to set up your development environment. This Quick Start guide assumes you have a Solana program with Arcium integration and are familiar with Arcium and Solana concepts.
One-Time Setup: Initialize Your Computation Definition
Before you can submit a computation, register and upload its definition once per circuit — after deploying your program. This creates the on-chain computation-definition account and uploads the compiled circuit (the .arcis build artifact) so the network can execute it.
import * as from "@anchor-lang/core";
import { } from "@anchor-lang/core";
import { } from "fs";
import { } from "@arcium-hq/client";
import { } from "../target/types/your_program";
.setProvider(.AnchorProvider.env());
const = .getProvider() as .;
const = .workspace.YourProgram as <>;
// 1. Create the computation-definition account. The instruction name is derived
// from your encrypted function (e.g. `add_together` -> `initAddTogetherCompDef`).
await .methods
.initAddTogetherCompDef()
.accounts({
// compDefAccount, payer, mxeAccount, addressLookupTable
// — see the example below for the full account derivation
})
.rpc({ : "confirmed" });
// 2. Upload the compiled circuit to finalize the definition.
const = ("build/add_together.arcis");
await (
,
"add_together", // must match your encrypted instruction name
.programId,
);This runs once per circuit, not per computation. For the full account derivation (the comp-def PDA and lookup-table address), see the example programs and the Hello World guide.
Setup Anchor Provider
Configure your Anchor provider and get your MXE program ID. This establishes the connection to Solana and identifies your confidential computation program.
import * as from "@anchor-lang/core";
// Initialize Anchor provider from environment variables
.setProvider(.AnchorProvider.env());
const = .getProvider();
// Your MXE program ID
const = new .web3.PublicKey("YourProgramIdHere");Configure Cluster Account
Get the Arcium cluster account address. The cluster account represents a group of Arx nodes that will execute your confidential computations.
import { , } from "@arcium-hq/client";
// Get cluster offset from environment (set by `arcium localnet` or your .env)
const = ();
const = (.);getArciumEnv() reads ARCIUM_CLUSTER_OFFSET from environment variables. This is automatically set when running arcium localnet.
For devnet/testnet deployments, set ARCIUM_CLUSTER_OFFSET in your .env file. Get your cluster offset from the deployment guide.
Generate Keys and Derive Shared Secret
Perform x25519 key exchange with the MXE to derive a shared secret. This shared secret enables you to encrypt data that the MXE can then compute on.
import * as from "@anchor-lang/core";
import { , , } from "@arcium-hq/client";
import { } from "@solana/web3.js";
// Generate client keypair for encryption/decryption
// Note: In production, consider deriving this from your user keypair using signed message-based derivation
const = ..();
const = .();
// Fetch MXE public key with retry logic
// → Expand "Helper Functions" accordion below for full implementation
const = await getMXEPublicKeyWithRetry(
provider as .,
programId
);
// Derive shared secret and create cipher instance
const = .(, );
const = new ();Important
Security: Keep clientPrivateKey in memory until decryption completes. Never log or persist private keys.
Local Testing: Arx nodes generate MXE keys on startup, so getMXEPublicKey may initially return null. The getMXEPublicKeyWithRetry helper function below handles this with configurable retry logic (default: 20 retries × 500ms).
Helper Functions
Encrypt Input Data
Encrypt your computation inputs using the cipher from Step 3. Your sensitive data stays confidential throughout the entire computation.
import { } from "crypto";
import * as from "@anchor-lang/core";
import { } from "@arcium-hq/client";
// Prepare your computation inputs
const = [(42), (100)];
// Generate a 16-byte nonce
const = (16);
// Encrypt inputs - returns number[][] where ciphertext[i] corresponds to inputs[i]
// Each encrypted value is a 32-byte array
const = cipher.encrypt(, );
// Generate unique computation offset (8 random bytes as identifier)
const = new .BN((8), "hex");
// Convert nonce to BN for Solana transaction serialization
const = new .BN(().());- Input values must be less than the Curve25519 base field modulus (2^255 − 19), the field
RescueCipherencrypts over encrypt()takesbigint[]; convert other types first — e.g.BigInt(true)for a boolean, orBigInt(value)foru8–u128integersencrypt()returnsnumber[][]whereciphertext[i]is the encrypted form ofinputs[i]computationOffsetis a unique 8-byte identifier that tracks this computation on-chainnonceBNconverts the nonce bytes to a BigNumber for Solana transaction serialization
Set Up Event Listener
Load your Anchor program and set up the event listener before submitting the transaction. This ensures you can receive the encrypted results when they're ready.
import * as from "@anchor-lang/core";
import { } from "@anchor-lang/core";
import { } from "../target/types/your_program";
// Load your Anchor program using workspace pattern
const = .workspace.YourProgram as <>;
// Type-safe event listener helper
type = .<(typeof )["idl"]>;
const = async < extends keyof >(: ): <[]> => {
let : number;
const = await new <[]>(() => {
= .addEventListener(, () => {
();
});
});
await .removeEventListener();
return ;
};
// Set up event listener BEFORE submitting transaction
const = ("yourResultEvent");Set up the event listener before submitting the transaction to avoid race
conditions. The awaitEvent helper automatically cleans up after the event fires.
Submit Computation Transaction
Submit the confidential computation for execution. The Arcium network computes over your data without ever exposing it.
import * as from "@anchor-lang/core";
import {
,
,
,
,
,
,
} from "@arcium-hq/client";
// From previous steps:
// - provider, programId (Step 1)
// - arciumEnv, clusterAccount (Step 2)
// - clientPublicKey (Step 3)
// - computationOffset, ciphertext, nonceBN (Step 4)
// - program, resultEventPromise (Step 5)
// Get computation definition offset for your encrypted instruction
// The instruction name must match the function name in your Rust MXE program
// (e.g., if your function is `add_together`, use "add_together")
const = .(("your_instruction_name")).();
// Submit the computation transaction
const = await program.methods
.yourComputationMethod(
computationOffset,
.(ciphertext[0]),
.(ciphertext[1]),
.(clientPublicKey),
nonceBN
)
.accountsPartial({
: (arciumEnv.arciumClusterOffset, computationOffset),
,
: (programId),
: (arciumEnv.arciumClusterOffset),
: (arciumEnv.arciumClusterOffset),
: (programId, ),
// ... your program-specific accounts
})
.rpc({ : true, : "confirmed" });
.("Computation submitted:", );- Replace
yourComputationMethodwith your program's instruction name (must match your MXE function name) - Add your program-specific accounts toaccountsPartialalongside required Arcium accounts -clientPublicKeyis required for ECDH key exchange: we fetch the MXE public key usinggetMXEPublicKey, and sendclientPublicKeyto the MXE so it can derive the same shared secret. See encryption documentation for more details
Await Computation Finalization
Wait for the computation to finalize on-chain. This confirms that the MPC nodes have completed processing your confidential computation.
import * as from "@anchor-lang/core";
import { } from "@arcium-hq/client";
// Wait for computation finalization
const = await (
provider as .,
computationOffset,
programId,
"confirmed"
);
.("Computation finalized:", );Decrypt Results
After finalization completes, retrieve the event and decrypt the result. Use the same cipher instance from Step 3 to decrypt the encrypted output.
// Await the event (listener was set up in Step 5)
const = await resultEventPromise;
// Decrypt the result
// The result uses nonce + 1 (nonces must be unique per operation)
const = .(.nonce); // Convert from number[]
const = cipher.decrypt([.encryptedResult], )[0];
.("Decrypted result:", );Event structure: The resultEvent structure shown here (encryptedResult, nonce) is a generic example. Your actual event structure depends on your program's implementation and IDL definition.
Nonce handling: Convert the event's nonce to Uint8Array for decryption. The result nonce is your input nonce + 1 (nonces must be unique per operation to ensure cryptographic freshness).
Handling Failures
A computation can fail — for example, if the circuit can't be fetched or its on-chain hash doesn't match. Detect failures by listening for the Arcium program's failure event alongside your result event:
import * as from "@anchor-lang/core";
import { } from "@arcium-hq/client";
const = (provider as .);
const = .(
"finalizeFailureDataEvent",
() => {
.("Computation failed:", ..());
}
);
// ... submit your computation, then clean up once it finalizes:
await .();finalizeFailureDataEvent tells you a computation failed and carries its
computationOffset. The specific reason — such as OffChainCircuitFetchFailed or
CircuitCUMismatch — is surfaced by the callback when it finalizes, not by this event.
See the callback guide.
Complete Example
For a complete working example showing all steps together, see the Hello World guide which includes a full test file demonstrating the entire flow from encryption to decryption.
You can also explore the example programs repository:
arcium-hq/examples
What's Next
Explore the API
- Client API Reference: Detailed docs for encryption, PDA helpers, and encrypted instruction management.
- Reading & Monitoring On-Chain State: Use the Reader SDK to query accounts, subscribe to computation events, and read the mempool.
Dive Deeper
- Arcium Developer Docs: Framework internals, encrypted instruction design, deployment, and lifecycle.