Hello World
The Arcium tooling suite for writing MXEs (MPC eXecution Environments) is built on top of Anchor, so if you’re familiar with Anchor, you should find Arcium to be a familiar experience, except that you’re using thearcium CLI instead of anchor.
To initialize a new MXE project, run:
- The
Arcium.tomlfile, which contains the configuration for the Arcium tooling suite. - The
encrypted-ixsdirectory. This is where we write all our code that is meant to operate on encrypted data and therefore runs in MPC. This code is written using our own Rust framework called Arcis. This will already be populated with a simple example calledadd_together.rs. Let’s take a closer look at it.
Our first encrypted instruction
use arcis::*; imports all the necessary types and functions for writing encrypted instructions with Arcis. The #[encrypted] attribute marks a module that contains encrypted instructions. Inside this module, we define a struct InputValues that contains the two values we want to encrypt and pass to the encrypted instruction.
The #[instruction] macro marks the function as an entry point for MPC execution. While you can write helper functions without this attribute, only functions marked with #[instruction] will be compiled into individual circuits that can be called onchain.
The function add_together takes an encrypted input parameter of type Enc<Shared, InputValues>. Let’s break this down:
Enc<Owner, Data>is Arcium’s encrypted data typeSharedmeans the data is encrypted with a shared secret between the client and MXE (both can decrypt it)InputValuesis the actual data structure being encrypted (our struct with v1 and v2)- The alternative to
SharedisMxe, where only the MXE can decrypt the data
input_ctxt.to_arcis()converts the input into a form we can operate on within the MPC environment.- We perform the addition operation, casting the u8 values to u16 to prevent overflow.
input_ctxt.owner.from_arcis(sum)converts the encrypted sum into an encrypted format that can be stored onchain, while maintaining encryption with the shared secret between the client and the MXE.
Calling it from Solana
Now that we’ve written our first encrypted instruction, let’s see how we can use it from within a Solana program. Our default project already contains a Solana program in theprograms/ directory. Let’s take a closer look at it too:
InitAddTogetherCompDef, AddTogether, and AddTogetherCallback account structs are not included here, but they are automatically generated when you run arcium init. Here’s a simplified version of what AddTogether looks like:
#[arcium_program] macro (which replaces Anchor’s #[program] macro) and that for every encrypted instruction, we generally have three instructions in the Solana program:
init_add_together_comp_def: Initializes the computation definition. This runs once before the first invocation of the encrypted instruction. See Computation definition accounts.add_together: Invokes the encrypted instruction, builds the arguments, and queues the computation through the Arcium program. See Invoking computations.add_together_callback: Runs after the MPC cluster finishes the encrypted instruction and returns the result. See Invoking computations.
Building and testing
Similar to Anchor, encrypted instructions and Solana programs are built witharcium build. Tests use the @arcium-hq/client TypeScript library by default and run with arcium test. Install npm dependencies first with yarn or npm install in your project directory. By default, tests run against a local cluster. To test against devnet or mainnet, use arcium test --cluster devnet after adding the cluster offset to Arcium.toml, for example [clusters.devnet] offset = 456.
For faster local loops, use arcium test --test-name <name> to run tests/<name>.ts. After a full localnet keygen, arcium test --skip-keygen or arcium localnet --skip-keygen reuses cached MXE keys; for detached runs, call arcium snapshot-mxe-keygen --rpc-url l before shutting down the validator.
Let’s take a quick look at the default test file. Some helper functions and imports are excluded for brevity, but you can find the complete examples in your generated project:
initAddTogetherCompDef: Call theinit_add_together_comp_definstruction to initialize the encrypted instruction definition. (only need to be called once after the program is deployed)getMXEPublicKeyWithRetry: Fetch the MXE’s x25519 public key.x25519.utils.randomSecretKey: Generate a random private key for the x25519 key exchange.x25519.getPublicKey: Generate the public key corresponding to the private key we generated above.x25519.getSharedSecret: Generate the shared secret with the MXE cluster using a x25519 key exchange.cipher = new RescueCipher(sharedSecret): Initialize the Rescue cipher. The constructor derives a key from the Rescue-Prime hash function; see Encryption overview.cipher.encrypt: Encrypt the inputs for the encrypted instruction.awaitEvent: Wait for thesumEventevent to be emitted by the program on finalization of the computation (in the callback instruction).addTogether: Call theadd_togetherinstruction to invoke the encrypted instruction.awaitComputationFinalization: Since waiting for an Arcium computation is not the same as waiting for one Solana transaction (because the MPC cluster must finish the computation and invoke the callback), this function is used, which is provided by the Arcium TypeScript library.
Ready to Deploy?
Now that you have built and tested your MXE locally, you are probably eager to see it running live! Head over to our deployment guide where we’ll walk you through getting your MXE deployed on Solana. We’ll cover everything from choosing the right RPC endpoint to initializing your computation definitions.What’s next?
Core concepts
Understand MXEs, Clusters, and encrypted instructions.
Learn Arcis
Understand MPC constraints and build more complex circuits.