| Crates.io | inco-lightning |
| lib.rs | inco-lightning |
| version | 0.1.4 |
| created_at | 2026-01-08 13:41:18.85338+00 |
| updated_at | 2026-01-08 14:47:16.788689+00 |
| description | SDK for Inco Lightning |
| homepage | |
| repository | |
| max_upload_size | |
| id | 2030241 |
| size | 93,146 |
Program ID: 5sjEbPiqgZrYwR31ahR6Uk9wf5awoX61YGg7jExQSwaj
Add to your Cargo.toml:
[dependencies]
inco-lightning = { version = "0.1.0", features = ["cpi"] }
[programs.devnet]
inco_lightning = "5sjEbPiqgZrYwR31ahR6Uk9wf5awoX61YGg7jExQSwaj"
use anchor_lang::prelude::*;
use inco_lightning::cpi::accounts::Operation;
use inco_lightning::cpi::{e_add, e_sub, e_ge, e_select, new_euint128};
use inco_lightning::types::{Euint128, Ebool};
use inco_lightning::ID as INCO_LIGHTNING_ID;
#[derive(Accounts)]
pub struct MyInstruction<'info> {
#[account(mut)]
pub authority: Signer<'info>,
/// CHECK: Inco Lightning program for encrypted operations
#[account(address = INCO_LIGHTNING_ID)]
pub inco_lightning_program: AccountInfo<'info>,
}
Inco Lightning provides encrypted computation primitives for Solana programs. It enables confidential smart contracts where sensitive data remains encrypted throughout computation. The covalidator network processes encrypted operations off-chain while maintaining cryptographic guarantees.
Key benefits:
A handle is a 128-bit reference to an encrypted value stored off-chain by the covalidator network.
use inco_lightning::types::{Euint128, Ebool};
// Euint128 - handle to an encrypted unsigned 128-bit integer
pub struct Euint128(pub u128);
// Ebool - handle to an encrypted boolean
pub struct Ebool(pub u128);
Handles are deterministically derived from operations, so the same operation with the same inputs always produces the same handle. This allows programs to store and reference encrypted values without storing the actual ciphertext on-chain.
// Store a handle in your account
#[account]
pub struct ConfidentialAccount {
pub balance: Euint128, // Handle to encrypted balance
pub is_active: Ebool, // Handle to encrypted boolean
}
To create an encrypted value, use new_euint128 or new_ebool with client-encrypted ciphertext.
pub fn deposit(
ctx: Context<Deposit>,
ciphertext: Vec<u8>, // Client-encrypted amount
input_type: u8,
) -> Result<()> {
// Create CPI context
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
Operation {
signer: ctx.accounts.authority.to_account_info(),
},
);
// Create encrypted handle from ciphertext
let encrypted_amount: Euint128 = new_euint128(cpi_ctx, ciphertext, input_type)?;
// Store the handle
ctx.accounts.vault.balance = encrypted_amount;
Ok(())
}
Use as_euint128 and as_ebool to create encrypted handles from plaintext values. This is called "trivial encryption" because the value is known - useful for constants, thresholds, or initialization.
Note: Trivially encrypted values are deterministic - anyone can compute the same handle from the same plaintext. Use
new_euint128with client-encrypted ciphertext for truly private values.
| Function | Description | Signature |
|---|---|---|
as_euint128 |
Convert plaintext u128 to encrypted handle | (u128) -> Euint128 |
as_ebool |
Convert plaintext bool to encrypted handle | (bool) -> Ebool |
pub fn initialize_account(ctx: Context<Initialize>) -> Result<()> {
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
Operation {
signer: ctx.accounts.authority.to_account_info(),
},
);
// Create encrypted zero for initial balance
let zero_balance: Euint128 = as_euint128(cpi_ctx, 0)?;
ctx.accounts.account.balance = zero_balance;
Ok(())
}
pub fn check_minimum_balance(
ctx: Context<CheckBalance>,
balance: Euint128,
) -> Result<()> {
let inco = ctx.accounts.inco_lightning_program.to_account_info();
let signer = ctx.accounts.authority.to_account_info();
// Create encrypted threshold (e.g., 100 tokens with 6 decimals)
let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let min_threshold: Euint128 = as_euint128(cpi_ctx, 100_000_000)?;
// Compare balance against threshold
let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let meets_minimum: Ebool = e_ge(cpi_ctx, balance, min_threshold, 0)?;
// Use result in e_select or store it
ctx.accounts.account.meets_minimum = meets_minimum;
Ok(())
}
Use e_rand to generate cryptographically secure random encrypted values.
| Function | Description | Signature |
|---|---|---|
e_rand |
Generate random encrypted value | (scalar_byte) -> Euint128 |
pub fn generate_random(ctx: Context<GenerateRandom>) -> Result<()> {
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
Operation {
signer: ctx.accounts.authority.to_account_info(),
},
);
// Generate random encrypted value
let random_value: Euint128 = e_rand(cpi_ctx, 0)?;
ctx.accounts.game.random_seed = random_value;
Ok(())
}
Inco Lightning provides arithmetic and comparison operations on encrypted values.
| Function | Description | Signature |
|---|---|---|
e_add |
Addition | (Euint128, Euint128) -> Euint128 |
e_sub |
Subtraction | (Euint128, Euint128) -> Euint128 |
e_mul |
Multiplication | (Euint128, Euint128) -> Euint128 |
e_rem |
Remainder | (Euint128, Euint128) -> Euint128 |
| Function | Description | Signature |
|---|---|---|
e_ge |
Greater than or equal | (Euint128, Euint128) -> Ebool |
e_gt |
Greater than | (Euint128, Euint128) -> Ebool |
e_le |
Less than or equal | (Euint128, Euint128) -> Ebool |
e_lt |
Less than | (Euint128, Euint128) -> Ebool |
e_eq |
Equal | (Euint128, Euint128) -> Ebool |
| Function | Description | Signature |
|---|---|---|
e_and |
Bitwise AND | (Euint128, Euint128) -> Euint128 |
e_or |
Bitwise OR | (Euint128, Euint128) -> Euint128 |
e_not |
Bitwise NOT | (Euint128) -> Euint128 |
e_shl |
Shift left | (Euint128, Euint128) -> Euint128 |
e_shr |
Shift right | (Euint128, Euint128) -> Euint128 |
pub fn add_balances(
ctx: Context<AddBalances>,
amount_a: Euint128,
amount_b: Euint128,
) -> Result<()> {
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
Operation {
signer: ctx.accounts.authority.to_account_info(),
},
);
// Add two encrypted values
let result: Euint128 = e_add(cpi_ctx, amount_a, amount_b, 0)?;
ctx.accounts.account.total = result;
Ok(())
}
The e_select function enables conditional logic on encrypted values without revealing the condition.
e_select(condition: Ebool, if_true: Euint128, if_false: Euint128) -> Euint128
Returns if_true when condition is encrypted true, if_false otherwise.
pub fn confidential_transfer(
ctx: Context<Transfer>,
transfer_amount: Euint128,
) -> Result<()> {
let inco = ctx.accounts.inco_lightning_program.to_account_info();
let signer = ctx.accounts.authority.to_account_info();
let source_balance = ctx.accounts.source.balance;
let dest_balance = ctx.accounts.destination.balance;
// Check if source has sufficient balance (encrypted comparison)
let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let has_sufficient: Ebool = e_ge(cpi_ctx, source_balance, transfer_amount, 0)?;
// Create zero for failed transfer case
let zero = Euint128::wrap(0);
// Select actual transfer amount: if sufficient balance, use amount; else use 0
let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let actual_amount: Euint128 = e_select(cpi_ctx, has_sufficient, transfer_amount, zero, 0)?;
// Subtract from source (will subtract 0 if insufficient)
let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let new_source_balance: Euint128 = e_sub(cpi_ctx, source_balance, actual_amount, 0)?;
// Add to destination (will add 0 if insufficient)
let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let new_dest_balance: Euint128 = e_add(cpi_ctx, dest_balance, actual_amount, 0)?;
// Update balances
ctx.accounts.source.balance = new_source_balance;
ctx.accounts.destination.balance = new_dest_balance;
Ok(())
}
This pattern ensures:
Use allow and is_allowed to manage decryption permissions for handles.
pub fn grant_decrypt_access(
ctx: Context<GrantAccess>,
handle: u128,
allowed_address: Pubkey,
) -> Result<()> {
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
Allow {
allowance_account: ctx.accounts.allowance_account.to_account_info(),
signer: ctx.accounts.authority.to_account_info(),
allowed_address: ctx.accounts.allowed_address.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
},
);
allow(cpi_ctx, handle, true, allowed_address)?;
Ok(())
}
pub fn check_access(
ctx: Context<CheckAccess>,
handle: u128,
) -> Result<bool> {
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
IsAllowed {
allowance_account: ctx.accounts.allowance_account.to_account_info(),
allowed_address: ctx.accounts.allowed_address.to_account_info(),
},
);
is_allowed(cpi_ctx, handle)
}
The is_validsignature function verifies Ed25519 signatures for attested decryption results.
pub fn verify_decryption(
ctx: Context<VerifyDecryption>,
handles: Vec<Vec<u8>>,
plaintext_values: Vec<Vec<u8>>,
) -> Result<()> {
let cpi_ctx = CpiContext::new(
ctx.accounts.inco_lightning_program.to_account_info(),
VerifySignature {
instructions: ctx.accounts.instructions.to_account_info(),
signer: ctx.accounts.authority.to_account_info(),
},
);
// Verify Ed25519 signatures from previous instruction
// The covalidator signs hash(handle + plaintext_value)
let results = is_validsignature(
cpi_ctx,
1, // expected signature count
Some(handles), // handles being verified
Some(plaintext_values), // claimed plaintext values
)?;
// If we get here, signatures are valid
// Use the verified plaintext values
Ok(())
}
Never branch on decrypted values in your program. Use e_select to keep logic encrypted:
// BAD: Leaks information through control flow
if decrypted_balance > amount {
transfer(amount);
}
// GOOD: Encrypted conditional
let sufficient = e_ge(ctx, balance, amount, 0)?;
let actual = e_select(ctx, sufficient, amount, zero, 0)?;
e_sub(ctx, balance, actual, 0)?;
Always check if handles are initialized before operations:
if !handle.is_initialized() {
// Handle is zero/uninitialized
return Err(ErrorCode::UninitializedHandle.into());
}
Grant minimal decryption permissions:
// Only allow the account owner to decrypt their balance
allow(ctx, balance_handle, true, owner_pubkey)?;
Handles are 16 bytes. Store them on-chain; ciphertext lives off-chain:
#[account]
pub struct Vault {
pub owner: Pubkey, // 32 bytes
pub balance: Euint128, // 16 bytes (handle only!)
pub bump: u8,
}