Skip to main content

Step-By-Step Guide

1

Write your program

Write your Solana program as you normally.
2

Add delegation hooks in your program

Add CPI hooks to delegate, commit and undelegate state accounts through Ephemeral Rollup sessions.
3

Add restrictions through permission hooks in your program.

Add CPI hooks that create groups and permissions that enable restrictions for specific delegated accounts.
4

Deploy your program on Solana

Deploy your Solana program using Anchor CLI.
5

Implement authorization in your client

Sign user message to retrieve authorization token from TEE endpoint.
6

Execute transactions and test privacy

Request for authorization token and send confidential transactions.

Private Transfer Example

Private Transfer GIF The following software packages may be required, other versions may also be compatible:
SoftwareVersionInstallation Guide
Solana2.1.21Install Solana
Rust1.82.0Install Rust
Anchor0.31.1Install Anchor
Node22.17.0Install Node

Quick Access

Find the full private transfer example with Anchor framework and client implementation:

Code Snippets

  • 1. Write program
  • 2. Delegate
  • 3. Restrict
  • 4. Deploy
  • 5. Authorize
  • 6. Test
A simple transfer program where tokens are tracked through deposit accounts, and locked in a vault account:
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token::{Mint, Token, TokenAccount};

/// ... other imports for delegation and privacy

declare_id!("EnhkomtzKms55jXi3ijn9XsMKYpMT4BJjmbuDQmPo3YS");

pub const DEPOSIT_PDA_SEED: &[u8] = b"deposit";
pub const VAULT_PDA_SEED: &[u8] = b"vault";

#[program]
pub mod private_payments {
    use anchor_spl::token::{transfer_checked, TransferChecked};

    use super::*;

    /// Initializes a deposit account for a user and token mint if it does not exist.
    ///
    /// Sets up a new deposit account with zero balance for the user and token mint.
    pub fn initialize_deposit(ctx: Context<InitializeDeposit>) -> Result<()> {
        let deposit = &mut ctx.accounts.deposit;
        deposit.set_inner(Deposit {
            user: ctx.accounts.user.key(),
            token_mint: ctx.accounts.token_mint.key(),
            amount: 0,
        });

        Ok(())
    }

    /// Modifies the balance of a user's deposit account by transferring tokens in or out.
    ///
    /// If `args.increase` is true, tokens are transferred from the user's token account to the deposit account.
    /// If false, tokens are transferred from the deposit account back to the user's token account.
    pub fn modify_balance(ctx: Context<ModifyDeposit>, args: ModifyDepositArgs) -> Result<()> {
        let deposit = &mut ctx.accounts.deposit;

        if args.increase {
            transfer_checked(
                CpiContext::new(
                    ctx.accounts.token_program.to_account_info(),
                    TransferChecked {
                        from: ctx.accounts.user_token_account.to_account_info(),
                        mint: ctx.accounts.token_mint.to_account_info(),
                        to: ctx.accounts.vault_token_account.to_account_info(),
                        authority: ctx.accounts.user.to_account_info(),
                    },
                ),
                args.amount,
                ctx.accounts.token_mint.decimals,
            )?;
            deposit.amount += args.amount;
        } else {
            let seeds = [
                VAULT_PDA_SEED,
                &ctx.accounts.token_mint.key().to_bytes(),
                &[ctx.bumps.vault]
            ];
            let signer_seeds = &[&seeds[..]];
            transfer_checked(
                CpiContext::new_with_signer(
                    ctx.accounts.token_program.to_account_info(),
                    TransferChecked {
                        from: ctx.accounts.vault_token_account.to_account_info(),
                        mint: ctx.accounts.token_mint.to_account_info(),
                        to: ctx.accounts.user_token_account.to_account_info(),
                        authority: ctx.accounts.vault.to_account_info(),
                    },
                    signer_seeds,
                ),
                args.amount,
                ctx.accounts.token_mint.decimals,
            )?;
            deposit.amount -= args.amount;
        }

        Ok(())
    }

    /// ... Other instructions for delegation and privacy
}

/// Context for InitializeDeposit
#[derive(Accounts)]
pub struct InitializeDeposit<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    /// CHECK: Anyone can initialize the deposit
    pub user: UncheckedAccount<'info>,
    #[account(
        init_if_needed,
        payer = payer,
        space = 8 + Deposit::INIT_SPACE,
        seeds = [DEPOSIT_PDA_SEED, user.key().as_ref(), token_mint.key().as_ref()],
        bump
    )]
    pub deposit: Account<'info, Deposit>,
    pub token_mint: Account<'info, Mint>,
    pub token_program: Program<'info, Token>,
    pub system_program: Program<'info, System>,
}

/// Context args for InitializeDeposit
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ModifyDepositArgs {
    pub amount: u64,
    pub increase: bool,
}

/// Context for ModifyDeposit
#[derive(Accounts)]
pub struct ModifyDeposit<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    pub user: Signer<'info>,
    #[account(
        init_if_needed,
        payer = payer,
        space = 8 + Vault::INIT_SPACE,
        seeds = [VAULT_PDA_SEED, deposit.token_mint.as_ref()],
        bump,
    )]
    pub vault: Account<'info, Vault>,
    #[account(
        mut,
        seeds = [DEPOSIT_PDA_SEED, deposit.user.as_ref(), deposit.token_mint.as_ref()],
        bump,
        has_one = user,
        has_one = token_mint,
    )]
    pub deposit: Account<'info, Deposit>,
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = token_mint,
        associated_token::authority = user,
    )]
    pub user_token_account: Account<'info, TokenAccount>,
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = token_mint,
        associated_token::authority = vault,
    )]
    pub vault_token_account: Account<'info, TokenAccount>,
    pub token_mint: Account<'info, Mint>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}

/// A deposit account for a user and token mint.
#[account]
#[derive(InitSpace)]
pub struct Deposit {
    pub user: Pubkey,
    pub token_mint: Pubkey,
    pub amount: u64,
}

/// A vault storing deposited tokens.
/// Has a dummy field because Anchor requires it.
#[account]
#[derive(InitSpace)]
pub struct Vault {
    _dummy: u8,
}


/// ... Other context and accounts for delegation and privacy

⬆️ Back to Top

Solana Explorer

Get insights about your transactions and accounts on Solana:

Solana RPC Providers

Send transactions and requests through existing RPC providers:

Solana Validator Dashboard

Find real-time updates on Solana’s validator infrastructure:

Server Status

Subscribe to Solana’s and MagicBlock’s server status:

MagicBlock Products