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
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 on-chain 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]Encrypted boolean encrypted_u8(value)[u8; 32]Encrypted unsigned 8-bit encrypted_i8(value)[u8; 32]Encrypted signed 8-bit encrypted_u16(value)[u8; 32]Encrypted unsigned 16-bit encrypted_i16(value)[u8; 32]Encrypted signed 16-bit encrypted_u32(value)[u8; 32]Encrypted unsigned 32-bit encrypted_i32(value)[u8; 32]Encrypted signed 32-bit encrypted_u64(value)[u8; 32]Encrypted unsigned 64-bit encrypted_i64(value)[u8; 32]Encrypted signed 64-bit encrypted_u128(value)[u8; 32]Encrypted unsigned 128-bit encrypted_i128(value)[u8; 32]Encrypted signed 128-bit encrypted_float(value)[u8; 32]Encrypted floating point
Method Input Type Description x25519_pubkey(value)[u8; 32]X25519 public key for encryption arcis_ed25519_signature(value)[u8; 64]Ed25519 signature
Method Input Type Description account(pubkey, offset, length)Pubkey, u32, u32Reference on-chain account data
9. Update on-chain 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 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
11. (Optional) Update callback macros for multi-transaction callbacks
If you need multiple callback functions for the same encrypted 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 (())
}
12. 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
13. 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 (())
}
#[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.
14. 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)