Arcium LogoArcium TS SDK Docs

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/client

Overview

This guide will walk you through the complete flow of using the Arcium TypeScript SDK to submit and process confidential computations:

  1. Setup: Configure your Anchor provider and Arcium cluster connection
  2. Encrypt: Establish encryption keys and encrypt your computation inputs
  3. Submit: Send confidential computations to the Arcium network
  4. Monitor: Track computation finalization on-chain
  5. 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 RescueCipher encrypts over
  • encrypt() takes bigint[]; convert other types first — e.g. BigInt(true) for a boolean, or BigInt(value) for u8u128 integers
  • encrypt() returns number[][] where ciphertext[i] is the encrypted form of inputs[i]
  • computationOffset is a unique 8-byte identifier that tracks this computation on-chain
  • nonceBN converts 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 yourComputationMethod with your program's instruction name (must match your MXE function name) - Add your program-specific accounts to accountsPartial alongside required Arcium accounts - clientPublicKey is required for ECDH key exchange: we fetch the MXE public key using getMXEPublicKey, and send clientPublicKey to 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:

GitHubarcium-hq/examples

3921

What's Next

Explore the API

Dive Deeper

On this page