How to Create a Confidential Token on Solana

The blockchain ecosystem is powered by tokens, enabling users to exchange value without intermediaries, participate in governance votes, and more.

However, since public blockchains make all transaction details visible to everyone, actions like sending tokens to a friend, getting paid a salary in tokens, or using DeFi protocols currently reveal the transfer amount and the participants' balances to the entire onchain world. This lack of privacy is a significant barrier to widespread adoption and is a clear disadvantage when comparing the use of blockchains to the conventional financial system. 

Inco is building a solution for onchain confidentiality. Currently live on Solana Devnet (in beta) and Base Sepolia testnet, Inco’s first product, Inco Lightning, offers developers a way to build confidential programs and smart contracts. 

One key use case of Inco Lightning is creating confidential tokens. In this demo, that’s what we’re going to show you how to do.

What Is a Confidential Token?

The Confidential Inco SPL Token Program is a confidential token implementation on Solana that enables confidentiality-preserving token operations. Unlike traditional SPL tokens, where balances and transfer amounts are publicly visible onchain, the Inco Token Program uses encryption to keep these values hidden while still allowing mathematical operations on them. So they behave like normal tokens on the Solana blockchain and can be used in Solana applications, but transaction metadata such as transaction amount and balance stays confidential.

How To Create a Confidential Token

Creating a confidential SPL token is easy. All you’ll need are your standard development tools, including Rust, Solana CLI, and Anchor. We’re going to deploy the contract to Solana Devnet.

Quickstart

Prerequisites

Before you get started with the contract, you’ll need to set up the prerequisites.

We recommend installing nvm (Node Version Manager) first and then install Node.js with nvm.

Configuring Solana for Devnet

# Set Solana to use devnet
solana config set --url devnet‍

# Verify configuration
solana config get‍

# Create a new wallet (if you don't have one)
solana-keygen new --outfile ~/.config/solana/id.json‍

# Get some devnet SOL for testing
solana airdrop 2‍

# Check your balance
solana balance‍

Environment Variables

Create a .env file in your project directory:

# Your Solana cluster
RPC_URL=https://api.devnet.solana.com‍

# Path to your wallet keypair
SOLANA_WALLET=~/.config/solana/id.json

Project Setup

# Create your project
git clone https://github.com/Inco-fhevm/lightning-rod-solana

# Install dependencies
yarn install‍

# Build the programs
anchor build

Configuring Anchor.toml File

[features]
resolution = true
skip-lint = false

[programs.devnet]
inco_token = "<your_program_id>"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "devnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

It is worth noting that you only need to add your own program ID here. The Inco Lightning program ID is handled internally by the inco-lightning crate via the INCO_LIGHTNING_ID constant.

Configuring Cargo.toml for Confidential Token

[package]
name = "confidential-token"
version = "0.1.0"
description = "Confidential Token Program"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "confidential_token"

[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []

[dependencies]
anchor-lang = { version = "0.31.1", features = ["init-if-needed"] }
anchor-spl = "0.31.1"
inco-lightning = { version = "0.1.4", features = ["cpi"] }

Building and Deployment

# Build all programs
anchor build

# Get your program ID
solana address -k target/deploy/confidential_token-keypair.json

# Update the program ID in Anchor.toml and lib.rs declare_id!()
# Then rebuild
anchor build

# Deploy to devnet
anchor deploy --provider.cluster devnet

# Verify deployment
solana program show <your-program-id>

After the deployment, don’t forget to update the declare_id! macro in your lib.rs with the actual deployed program ID.

Program Functions

Your confidential SPL token is compatible with these program functions.

Mint Operations

initialize_mint

Creates a new confidential token mint.

pub fn initialize_mint(
    ctx: Context<InitializeMint>,
    decimals: u8,                    // Token precision (e.g., 9)
    mint_authority: Pubkey,          // Authority to mint tokens
    freeze_authority: Option<Pubkey> // Authority to freeze accounts
) -> Result<()>

mint_to 

Mints confidential tokens to an account using encrypted ciphertext.

pub fn mint_to(
    ctx: Context<IncoMintTo>,
    ciphertext: Vec<u8>,  // Encrypted amount
    input_type: u8        // Encryption type identifier
) -> Result<()>

mint_to_with_handle

Mints confidential tokens using an existing encrypted handle.

pub fn mint_to_with_handle(
    ctx: Context<IncoMintTo>,
    amount_handle: Euint128  // Pre-existing encrypted handle
) -> Result<()>

Account Operations

initialize_account 

Creates a new confidential token account.

pub fn initialize_account(ctx: Context<InitializeAccount>) -> Result<()>

create

Creates an associated token account using PDA derivation.

pub fn create(ctx: Context<Create>) -> Result<()>

close_account 

Closes a token account and reclaims the rent.

pub fn close_account(ctx: Context<CloseAccount>) -> Result<()>

Transfer Operations

transfer

Transfers confidential tokens between accounts using encrypted ciphertext.

/// remaining_accounts:
///   [0] source_allowance_account (mut) - PDA for source owner's new balance
///   [1] source_owner_address (readonly)
///   [2] dest_allowance_account (mut) - PDA for destination owner's new balance
///   [3] dest_owner_address (readonly)
pub fn transfer<'info>(
    ctx: Context<'_, '_, '_, 'info, IncoTransfer<'info>>,
    ciphertext: Vec<u8>,  // Encrypted transfer amount
    input_type: u8        // Encryption type identifier
) -> Result<()>

Burn Operations

burn

/// remaining_accounts:
///   [0] allowance_account (mut) - PDA for granting decrypt access to owner
///   [1] owner_address (readonly)
pub fn burn<'info>(
    ctx: Context<'_, '_, '_, 'info, IncoBurn<'info>>,
    ciphertext: Vec<u8>,  // Encrypted burn amount
    input_type: u8        // Encryption type identifier
) -> Result<()>

Also you can find the complete implementation of the confidential token program on our Github.

Running Tests

If you want to test your deployed program, all you need to do is simply running the test command:

# Build the program
anchor build

# Run tests on devnet
anchor test --provider.cluster devnet

Next.js Template

If you want to go further and build confidential Solana dApps, there is a very easy way of doing so. We have built a quickstart starter template that helps developers build a frontend for confidential Solana applications using the Inco Solana SDK and Solana wallet adapters.

Prerequisites

Before you begin, make sure you have:

  • Node.js 18+ installed
  • A Solana wallet (Phantom, Solflare, etc.)
  • Some devnet SOL for testing

If you don’t have any Devnet SOL, you can get some from the Solana Faucet, or simply run the command below in your terminal:

solana airdrop 2

# Clone the template
git clone https://github.com/Inco-fhevm/nextjs-template-svm.git
cd nextjs-template-svm

# Install dependencies
npm install
# or
yarn install
# or
bun install

# Start the development server
npm run dev
# or: yarn dev / bun run dev

After running the “npm run dev” or “yarn dev” command, open http://localhost:3000 on your browser to see your app.

Conclusion: How Easy Was That?

By this point you’ll have your confidential token program in production on Solana Devnet, enabling users to transact confidentially, and you’ll also have an understanding of the program logic. It’s important to note that this is far from the only use case possible with a confidential program: theoretically, you could build any blockchain application you can think of.

Want to go further building with Inco? Check out the documentation, schedule a call with the Inco dev rel team, and join the Inco Discord and check out the dev-chat channel.

Incoming newsletter

Stay up to date with the latest on FHE and onchain confidentiality.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.