use inco_lightning::cpi::accounts::{Operation, Allow};
use inco_lightning::cpi::{e_add, e_sub, e_ge, e_select, as_euint128, allow};
/// remaining_accounts:
/// [0] source_allowance_account (mut)
/// [1] source_owner_address (readonly)
/// [2] dest_allowance_account (mut)
/// [3] dest_owner_address (readonly)
pub fn confidential_transfer<'info>(
ctx: Context<'_, '_, '_, 'info, Transfer<'info>>,
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 cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let zero = as_euint128(cpi_ctx, 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;
// Grant allowance to source owner for their new balance
if ctx.remaining_accounts.len() >= 2 {
let cpi_ctx = CpiContext::new(
inco.clone(),
Allow {
allowance_account: ctx.remaining_accounts[0].clone(),
signer: signer.clone(),
allowed_address: ctx.remaining_accounts[1].clone(),
system_program: ctx.accounts.system_program.to_account_info(),
}
);
allow(cpi_ctx, new_source_balance.0, true, ctx.accounts.source.owner)?;
}
// Grant allowance to destination owner for their new balance
if ctx.remaining_accounts.len() >= 4 {
let cpi_ctx = CpiContext::new(
inco.clone(),
Allow {
allowance_account: ctx.remaining_accounts[2].clone(),
signer: signer.clone(),
allowed_address: ctx.remaining_accounts[3].clone(),
system_program: ctx.accounts.system_program.to_account_info(),
}
);
allow(cpi_ctx, new_dest_balance.0, true, ctx.accounts.destination.owner)?;
}
Ok(())
}
#[derive(Accounts)]
pub struct Transfer<'info> {
#[account(mut)]
pub authority: Signer<'info>,
#[account(mut)]
pub source: Account<'info, TokenAccount>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
/// CHECK: Inco Lightning program
#[account(address = INCO_LIGHTNING_ID)]
pub inco_lightning_program: AccountInfo<'info>,
pub system_program: Program<'info, System>,
}