Documentation Index Fetch the complete documentation index at: https://docs.arcium.com/llms.txt
Use this file to discover all available pages before exploring further.
1. Arcium.toml backends (no change required)
Arcium.toml stays the same for this migration. Only the Cerberus backend is supported at the moment; Manticore is unavailable. If your config already uses Cerberus, no action is needed.
2. Update Arcium Rust dependencies
Update your dependencies to v0.5.1:
# Update program dependencies
cd programs/your-program-name
cargo update --package arcium-client --precise 0.5.1
cargo update --package arcium-macros --precise 0.5.1
cargo update --package arcium-anchor --precise 0.5.1
# Update encrypted-ixs dependencies
cd ../../encrypted-ixs
cargo update --package arcis-imports --precise 0.5.1
3. Update Arcium TS dependencies
npm install @arcium-hq/client@0.5.1
4. Update TypeScript environment variable usage
The environment variable and type have changed from a public key to a cluster offset:
// Before v0.5.1
const arciumEnv = getArciumEnv ();
console . log ( arciumEnv . arciumClusterPubkey ); // PublicKey
// v0.5.1
const arciumEnv = getArciumEnv ();
console . log ( arciumEnv . arciumClusterOffset ); // number
Environment variable change:
Before: ARCIUM_CLUSTER_PUBKEY (base58 pubkey string)
After: ARCIUM_CLUSTER_OFFSET (integer)
5. Update TypeScript SDK function names
Several functions have been renamed for consistency:
Program Address Function:
// Before v0.5.1
import { getArciumProgAddress } from "@arcium-hq/client" ;
const address = getArciumProgAddress ();
// v0.5.1
import { getArciumProgramId } from "@arcium-hq/client" ;
const address = getArciumProgramId ();
Account Data Functions:
// Before v0.5.1
const mempool = await getMempoolAccData ( provider , address );
const execpool = await getExecutingPoolAccData ( provider , address );
// v0.5.1
const mempool = await getMempoolAccInfo ( provider , address );
const execpool = await getExecutingPoolAccInfo ( provider , address );
Parameter Name Updates:
Functions now use mxeProgramId (camelCase) instead of mxeProgramID:
// Before v0.5.1
await getMXEPublicKey ( provider , mxeProgramID );
await uploadCircuit ( provider , name , mxeProgramID , circuit );
// v0.5.1
await getMXEPublicKey ( provider , mxeProgramId );
await uploadCircuit ( provider , name , mxeProgramId , circuit );
Full Function Rename Reference
Old Function New Function getArciumProgAddress()getArciumProgramId()getMempoolAccData()getMempoolAccInfo()getExecutingPoolAccData()getExecutingPoolAccInfo()getArciumProgramReadonly()getArciumProgram()getArxAccPDA()getArxNodeAccAddress()
6. Update TypeScript PDA derivation functions
Several PDA derivation functions now take clusterOffset instead of programId.
Note: getMXEAccAddress() still uses program.programId - only cluster-related PDAs changed.
// Before v0.5.1
import {
getMXEAccAddress ,
getMempoolAccAddress ,
getExecutingPoolAccAddress ,
getComputationAccAddress ,
} from "@arcium-hq/client" ;
. accountsPartial ({
computationAccount: getComputationAccAddress (
program . programId ,
computationOffset
),
clusterAccount: arciumEnv . arciumClusterPubkey ,
mxeAccount: getMXEAccAddress ( program . programId ),
mempoolAccount: getMempoolAccAddress ( program . programId ),
executingPool: getExecutingPoolAccAddress ( program . programId ),
})
// v0.5.1
import {
getMXEAccAddress ,
getMempoolAccAddress ,
getExecutingPoolAccAddress ,
getComputationAccAddress ,
getClusterAccAddress , // New import
} from "@arcium-hq/client" ;
. accountsPartial ({
computationAccount: getComputationAccAddress (
arciumEnv . arciumClusterOffset , // Changed from program.programId
computationOffset
),
clusterAccount: getClusterAccAddress ( arciumEnv . arciumClusterOffset ), // Now derived
mxeAccount: getMXEAccAddress ( program . programId ), // Unchanged
mempoolAccount: getMempoolAccAddress ( arciumEnv . arciumClusterOffset ), // Changed
executingPool: getExecutingPoolAccAddress ( arciumEnv . arciumClusterOffset ), // Changed
})
7. Update queue_computation call
The queue_computation function has two changes:
New parameter : cu_price_micro: u64 for priority fees
Updated callback_ix : Now requires computation_offset and mxe_account
// Before v0.5.1
queue_computation (
ctx . accounts,
computation_offset ,
args ,
None ,
vec! [ MyCallback :: callback_ix ( & [])],
1 ,
) ? ;
// v0.5.1
queue_computation (
ctx . accounts,
computation_offset ,
args ,
None ,
vec! [ MyCallback :: callback_ix (
computation_offset ,
& ctx . accounts . mxe_account,
& []
) ? ],
1 ,
0 , // cu_price_micro: priority fee in microlamports (0 = no priority fee)
) ? ;
Priority Fee Notes:
Set cu_price_micro to 0 for standard processing
Use higher values (e.g., 50000) for faster processing during network congestion
8. Update onchain argument API
Replace the vec![Argument::...] pattern with the new ArgBuilder API:
// Before v0.5.1
let args = vec! [
Argument :: ArcisPubkey ( pub_key ),
Argument :: PlaintextU128 ( nonce ),
Argument :: EncryptedU8 ( ciphertext_0 ),
Argument :: EncryptedU8 ( ciphertext_1 ),
];
// v0.5.1
let args = ArgBuilder :: new ()
. x25519_pubkey ( pub_key )
. plaintext_u128 ( nonce )
. encrypted_u8 ( ciphertext_0 )
. encrypted_u8 ( ciphertext_1 )
. build ();
Available ArgBuilder methods
Method Input Type Description plaintext_bool(value)boolBoolean value plaintext_u8(value)u8Unsigned 8-bit integer plaintext_i8(value)i8Signed 8-bit integer plaintext_u16(value)u16Unsigned 16-bit integer plaintext_i16(value)i16Signed 16-bit integer plaintext_u32(value)u32Unsigned 32-bit integer plaintext_i32(value)i32Signed 32-bit integer plaintext_u64(value)u64Unsigned 64-bit integer plaintext_i64(value)i64Signed 64-bit integer plaintext_u128(value)u128Unsigned 128-bit integer plaintext_i128(value)i128Signed 128-bit integer plaintext_float(value)f64Floating point plaintext_point(value)[u8; 32]Elliptic curve point (new in v0.5.1)
Method Input Type Description encrypted_bool(value)[u8; 32]Confidential boolean encrypted_u8(value)[u8; 32]Confidential unsigned 8-bit encrypted_i8(value)[u8; 32]Confidential signed 8-bit encrypted_u16(value)[u8; 32]Confidential unsigned 16-bit encrypted_i16(value)[u8; 32]Confidential signed 16-bit encrypted_u32(value)[u8; 32]Confidential unsigned 32-bit encrypted_i32(value)[u8; 32]Confidential signed 32-bit encrypted_u64(value)[u8; 32]Confidential unsigned 64-bit encrypted_i64(value)[u8; 32]Confidential signed 64-bit encrypted_u128(value)[u8; 32]Confidential unsigned 128-bit encrypted_i128(value)[u8; 32]Confidential signed 128-bit encrypted_float(value)[u8; 32]Confidential floating point
Method Input Type Description x25519_pubkey(value)[u8; 32]X25519 public key for confidentiality arcis_ed25519_signature(value)[u8; 64]Ed25519 signature
Method Input Type Description account(pubkey, offset, length)Pubkey, u32, u32Reference onchain account data
9. Update onchain PDA macros
PDA derivation macros now require mxe_account and an error code parameter:
// Before v0.5.1
#[account(
mut ,
address = derive_mempool_pda ! ()
)]
pub mempool_account : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_execpool_pda ! ()
)]
pub executing_pool : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_comp_pda ! (computation_offset)
)]
pub computation_account : UncheckedAccount <' info >,
Don’t forget to update the cluster PDA as well:
// Before v0.5.1
#[account(
mut ,
address = derive_cluster_pda ! ()
)]
pub cluster_account : Account <' info , Cluster >,
// v0.5.1
#[account(
mut ,
address = derive_cluster_pda ! (mxe_account, ErrorCode :: ClusterNotSet )
)]
pub cluster_account : Account <' info , Cluster >,
// v0.5.1
#[account(
mut ,
address = derive_mempool_pda ! (mxe_account, ErrorCode :: ClusterNotSet )
)]
pub mempool_account : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_execpool_pda ! (mxe_account, ErrorCode :: ClusterNotSet )
)]
pub executing_pool : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_comp_pda ! (
computation_offset,
mxe_account,
ErrorCode :: ClusterNotSet
)
)]
pub computation_account : UncheckedAccount <' info >,
Make sure you have the ClusterNotSet error code defined (this should already exist from v0.4.0):
#[error_code]
pub enum ErrorCode {
#[msg( "The computation was aborted" )]
AbortedComputation ,
#[msg( "Cluster not set" )]
ClusterNotSet ,
}
10. Update callback account structs
v0.5.1 requires three additional accounts in callback structs to support BLS signature verification:
// Before v0.5.1
#[callback_accounts( "my_circuit" )]
#[derive( Accounts )]
pub struct MyCircuitCallback <' info > {
pub arcium_program : Program <' info , Arcium >,
#[account(address = derive_comp_def_pda ! ( COMP_DEF_OFFSET_MY_CIRCUIT ))]
pub comp_def_account : Account <' info , ComputationDefinitionAccount >,
#[account(address = :: anchor_lang :: solana_program :: sysvar :: instructions :: ID )]
/// CHECK: instructions_sysvar
pub instructions_sysvar : AccountInfo <' info >,
}
// v0.5.1
#[callback_accounts( "my_circuit" )]
#[derive( Accounts )]
pub struct MyCircuitCallback <' info > {
pub arcium_program : Program <' info , Arcium >,
#[account(address = derive_comp_def_pda ! ( COMP_DEF_OFFSET_MY_CIRCUIT ))]
pub comp_def_account : Account <' info , ComputationDefinitionAccount >,
#[account(address = derive_mxe_pda ! ())]
pub mxe_account : Account <' info , MXEAccount >,
/// CHECK: computation_account, checked by arcium program via constraints in the callback context.
pub computation_account : UncheckedAccount <' info >,
#[account(address = derive_cluster_pda ! (mxe_account, ErrorCode :: ClusterNotSet ))]
pub cluster_account : Account <' info , Cluster >,
#[account(address = :: anchor_lang :: solana_program :: sysvar :: instructions :: ID )]
/// CHECK: instructions_sysvar
pub instructions_sysvar : AccountInfo <' info >,
}
New Required Accounts:
mxe_account - The MXE program account, derived using derive_mxe_pda!()
computation_account - The computation account (unchecked, verified by Arcium program)
cluster_account - The cluster account, derived from mxe_account
The order matters - cluster_account must come after mxe_account since its PDA derivation depends on the MXE account reference.
These accounts are required for the verify_output() call in your callback handler (see next section).
11. Update callback handling with BLS verification
v0.5.1 introduces BLS signature verification for computation outputs. Use SignedComputationOutputs and call verify_output() to validate results:
// Before v0.5.1 - Direct output handling
#[arcium_callback(encrypted_ix = "my_circuit" )]
pub fn my_circuit_callback (
ctx : Context < MyCircuitCallback >,
output : ComputationOutputs < MyCircuitOutput >,
) -> Result <()> {
let result = output . unwrap ();
// Use result directly...
Ok (())
}
// v0.5.1 - BLS verified outputs
#[arcium_callback(encrypted_ix = "my_circuit" )]
pub fn my_circuit_callback (
ctx : Context < MyCircuitCallback >,
output : SignedComputationOutputs < MyCircuitOutput >,
) -> Result <()> {
// verify_output() validates the BLS signature from the MXE cluster
let result = match output . verify_output (
& ctx . accounts . cluster_account,
& ctx . accounts . computation_account
) {
Ok ( MyCircuitOutput { field_0 }) => field_0 ,
Err ( e ) => {
msg! ( "Computation verification failed: {}" , e );
return Err ( ErrorCode :: AbortedComputation . into ())
},
};
// Now use the verified result
emit! ( ResultEvent {
value : result . ciphertexts[ 0 ],
nonce : result . nonce . to_le_bytes (),
});
Ok (())
}
Key Changes:
Replace ComputationOutputs<T> with SignedComputationOutputs<T>
Always call verify_output() to validate BLS signatures
Handle the Result - verification can fail if signatures don’t match
verify_output() requires cluster_account and computation_account references
Error Codes:
BLSSignatureVerificationFailed - The BLS signature doesn’t match
InvalidClusterBLSPublicKey - Cluster’s BLS key is not set
AbortedComputation - The computation failed in the MXE
12. (Optional) Update callback macros for multi-transaction callbacks
If you need multiple callback functions for the same confidential instruction, use the new skip_name_validation parameter:
// Primary callback (standard naming)
#[arcium_callback(encrypted_ix = "add_order" )]
pub fn add_order_callback (
ctx : Context < AddOrderCallback >,
output : SignedComputationOutputs < AddOrderOutput >,
) -> Result <()> {
// Handle primary callback with BLS verification
let result = output . verify_output (
& ctx . accounts . cluster_account,
& ctx . accounts . computation_account
) ? ;
Ok (())
}
// Secondary callback (non-standard naming)
#[arcium_callback(
encrypted_ix = "add_order" ,
auto_serialize = false,
skip_name_validation = true
)]
pub fn emit_order_event (
ctx : Context < EmitOrderEvent >,
output : RawComputationOutputs < ShortVec < u8 >>
) -> Result <()> {
// Handle secondary callback (e.g., emit event)
Ok (())
}
13. (Optional) Use circuit_hash! macro for offchain circuits
If using offchain circuit storage, replace placeholder hashes with verified hashes:
// Before v0.5.1
hash : [ 0 u8 ; 32 ],
// v0.5.1
use arcium_macros :: circuit_hash;
hash : circuit_hash! ( "my_circuit" ),
See Deployment: Handling Large Circuits for complete offchain circuit configuration.
14. Verify migration
After completing all migration steps, verify that everything works correctly:
Build test
# From your workspace root
arcium build
Type checking
# Ensure all new types compile correctly
cargo check --all
Test your changes
# Run your existing tests to ensure functionality is preserved
arcium test
15. Complete example
Here’s a complete before/after example of a typical computation function:
Before v0.5.1:
// Arcium.toml
[ localnet ]
nodes = 2
localnet_timeout_secs = 60
// programs/coinflip/src/lib.rs
pub fn flip (
ctx : Context < Flip >,
computation_offset : u64 ,
user_choice : [ u8 ; 32 ],
pub_key : [ u8 ; 32 ],
nonce : u128 ,
) -> Result <()> {
let args = vec! [
Argument :: ArcisPubkey ( pub_key ),
Argument :: PlaintextU128 ( nonce ),
Argument :: EncryptedU8 ( user_choice ),
];
ctx . accounts . sign_pda_account . bump = ctx . bumps . sign_pda_account;
queue_computation (
ctx . accounts,
computation_offset ,
args ,
None ,
vec! [ FlipCallback :: callback_ix ( & [])],
1 ,
) ? ;
Ok (())
}
#[queue_computation_accounts( "flip" , payer)]
#[derive( Accounts )]
#[instruction(computation_offset : u64 )]
pub struct Flip <' info > {
#[account( mut )]
pub payer : Signer <' info >,
#[account(
mut ,
address = derive_mempool_pda ! ()
)]
pub mempool_account : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_execpool_pda ! ()
)]
pub executing_pool : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_comp_pda ! (computation_offset)
)]
pub computation_account : UncheckedAccount <' info >,
// ... other accounts
}
// tests/coinflip.ts
const arciumEnv = getArciumEnv ();
await program . methods
. flip ( computationOffset , encryptedChoice , pubKey , nonce )
. accountsPartial ({
computationAccount: getComputationAccAddress ( program . programId , computationOffset ),
clusterAccount: arciumEnv . arciumClusterPubkey ,
mempoolAccount: getMempoolAccAddress ( program . programId ),
executingPool: getExecutingPoolAccAddress ( program . programId ),
})
. rpc ();
v0.5.1:
// Arcium.toml
[ localnet ]
nodes = 2
localnet_timeout_secs = 60
backends = [ "Cerberus" ]
// programs/coinflip/src/lib.rs
use arcium_anchor :: prelude ::* ;
const COMP_DEF_OFFSET_FLIP : u32 = comp_def_offset ( "flip" );
pub fn flip (
ctx : Context < Flip >,
computation_offset : u64 ,
user_choice : [ u8 ; 32 ],
pub_key : [ u8 ; 32 ],
nonce : u128 ,
) -> Result <()> {
let args = ArgBuilder :: new ()
. x25519_pubkey ( pub_key )
. plaintext_u128 ( nonce )
. encrypted_u8 ( user_choice )
. build ();
ctx . accounts . sign_pda_account . bump = ctx . bumps . sign_pda_account;
queue_computation (
ctx . accounts,
computation_offset ,
args ,
None ,
vec! [ FlipCallback :: callback_ix (
computation_offset ,
& ctx . accounts . mxe_account,
& []
) ? ],
1 ,
0 , // cu_price_micro: priority fee (0 = no priority)
) ? ;
Ok (())
}
// Callback with BLS verification
#[arcium_callback(encrypted_ix = "flip" )]
pub fn flip_callback (
ctx : Context < FlipCallback >,
output : SignedComputationOutputs < FlipOutput >,
) -> Result <()> {
let result = match output . verify_output (
& ctx . accounts . cluster_account,
& ctx . accounts . computation_account
) {
Ok ( FlipOutput { field_0 }) => field_0 ,
Err ( e ) => {
msg! ( "Error: {}" , e );
return Err ( ErrorCode :: AbortedComputation . into ())
},
};
emit! ( FlipResultEvent {
result : result . ciphertexts[ 0 ],
nonce : result . nonce . to_le_bytes (),
});
Ok (())
}
#[callback_accounts( "flip" )]
#[derive( Accounts )]
pub struct FlipCallback <' info > {
pub arcium_program : Program <' info , Arcium >,
#[account(address = derive_comp_def_pda ! ( COMP_DEF_OFFSET_FLIP ))]
pub comp_def_account : Account <' info , ComputationDefinitionAccount >,
#[account(address = derive_mxe_pda ! ())]
pub mxe_account : Account <' info , MXEAccount >,
/// CHECK: computation_account, checked by arcium program
pub computation_account : UncheckedAccount <' info >,
#[account(address = derive_cluster_pda ! (mxe_account, ErrorCode :: ClusterNotSet ))]
pub cluster_account : Account <' info , Cluster >,
#[account(address = :: anchor_lang :: solana_program :: sysvar :: instructions :: ID )]
/// CHECK: instructions_sysvar
pub instructions_sysvar : AccountInfo <' info >,
}
#[queue_computation_accounts( "flip" , payer)]
#[derive( Accounts )]
#[instruction(computation_offset : u64 )]
pub struct Flip <' info > {
#[account( mut )]
pub payer : Signer <' info >,
#[account(
mut ,
address = derive_mempool_pda ! (mxe_account, ErrorCode :: ClusterNotSet )
)]
pub mempool_account : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_execpool_pda ! (mxe_account, ErrorCode :: ClusterNotSet )
)]
pub executing_pool : UncheckedAccount <' info >,
#[account(
mut ,
address = derive_comp_pda ! (
computation_offset,
mxe_account,
ErrorCode :: ClusterNotSet
)
)]
pub computation_account : UncheckedAccount <' info >,
// ... other accounts
}
// tests/coinflip.ts
import {
getClusterAccAddress ,
getMXEAccAddress ,
getMempoolAccAddress ,
getExecutingPoolAccAddress ,
getComputationAccAddress ,
getCompDefAccAddress ,
getCompDefAccOffset ,
getArciumEnv ,
} from "@arcium-hq/client" ;
const arciumEnv = getArciumEnv ();
await program . methods
. flip ( computationOffset , encryptedChoice , pubKey , nonce )
. accountsPartial ({
computationAccount: getComputationAccAddress (
arciumEnv . arciumClusterOffset ,
computationOffset
),
clusterAccount: getClusterAccAddress ( arciumEnv . arciumClusterOffset ),
mxeAccount: getMXEAccAddress ( program . programId ),
mempoolAccount: getMempoolAccAddress ( arciumEnv . arciumClusterOffset ),
executingPool: getExecutingPoolAccAddress ( arciumEnv . arciumClusterOffset ),
compDefAccount: getCompDefAccAddress (
program . programId ,
Buffer . from ( getCompDefAccOffset ( "flip" )). readUInt32LE ()
),
})
. rpc ({ skipPreflight: true , commitment: "confirmed" });
That’s it! Your program should now be compatible with Arcium tooling v0.5.1.
16. Summary of breaking changes
Change Before v0.5.1 v0.5.1 queue_computation6 parameters 7 parameters (add cu_price_micro) callback_ixcallback_ix(&[])callback_ix(computation_offset, &mxe_account, &[])?Callback output ComputationOutputs<T>SignedComputationOutputs<T> with verify_output()PDA functions program.programIdarciumEnv.arciumClusterOffsetEnvironment ARCIUM_CLUSTER_PUBKEYARCIUM_CLUSTER_OFFSETPDA macros derive_*_pda!()derive_*_pda!(mxe_account, ErrorCode::ClusterNotSet)TS Program ID getArciumProgAddress()getArciumProgramId()TS Account Data getMempoolAccData()getMempoolAccInfo()TS Parameter mxeProgramIDmxeProgramId (camelCase)init_comp_definit_comp_def(accs, 0, None, None)init_comp_def(accs, None, None)ArgBuilder pubkey.arcis_x25519_pubkey(key).x25519_pubkey(key)Callback struct 3 accounts 6 accounts (add mxe_account, computation_account, cluster_account) Offchain circuit hash [0u8; 32] placeholdercircuit_hash!("circuit_name")