Skip to content

Instantly share code, notes, and snippets.

@yuhcee
Created August 28, 2025 16:03
Show Gist options
  • Save yuhcee/7c4385c574d8c4fd7e39fd80b1ed3f46 to your computer and use it in GitHub Desktop.
Save yuhcee/7c4385c574d8c4fd7e39fd80b1ed3f46 to your computer and use it in GitHub Desktop.
An Anchor PDA/CPI vault program for deposits and settlements
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, Token, TokenAccount, Transfer};
use anchor_spl::associated_token::{self, AssociatedToken};
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod pda_cpi_vault {
use super::*;
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
let cpi_accounts = Transfer {
from: ctx.accounts.user_token_account.to_account_info(),
to: ctx.accounts.vault_token_account.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;
Ok(())
}
pub fn settle(ctx: Context<Settle>, amount: u64, referrer_fee_basis_points: u16, merchant_fee_basis_points: u16) -> Result<()> {
let vault = &ctx.accounts.vault;
let seeds = &[b"vault".as_ref(), vault.merchant.as_ref(), &[vault.bump]];
let signer = &[&seeds[..]];
let merchant_amount = amount * u64::from(merchant_fee_basis_points) / 10000;
let referrer_amount = amount * u64::from(referrer_fee_basis_points) / 10000;
let fee_amount = amount - merchant_amount - referrer_amount;
// Transfer to merchant
let cpi_accounts = Transfer {
from: ctx.accounts.vault_token_account.to_account_info(),
to: ctx.accounts.merchant_token_account.to_account_info(),
authority: ctx.accounts.vault.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
token::transfer(cpi_ctx, merchant_amount)?;
// Transfer to referrer if present
if let Some(referrer_token_account) = &ctx.accounts.referrer_token_account {
let cpi_accounts = Transfer {
from: ctx.accounts.vault_token_account.to_account_info(),
to: referrer_token_account.to_account_info(),
authority: ctx.accounts.vault.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
token::transfer(cpi_ctx, referrer_amount)?;
}
// Transfer to fee receiver
let cpi_accounts = Transfer {
from: ctx.accounts.vault_token_account.to_account_info(),
to: ctx.accounts.fee_receiver_token_account.to_account_info(),
authority: ctx.accounts.vault.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
token::transfer(cpi_ctx, fee_amount)?;
Ok(())
}
}
#[account]
pub struct Vault {
pub merchant: Pubkey,
pub bump: u8,
}
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(mut)]
pub user_token_account: Account<'info, TokenAccount>,
#[account(
init_if_needed,
payer = user,
seeds = [b"vault".as_ref(), merchant.key().as_ref()],
bump,
space = 8 + 32 + 1
)]
pub vault: Account<'info, Vault>,
#[account(
init_if_needed,
payer = user,
associated_token::mint = mint,
associated_token::authority = vault
)]
pub vault_token_account: Account<'info, TokenAccount>,
pub merchant: SystemAccount<'info>,
pub mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Settle<'info> {
#[account(mut)]
pub owner: Signer<'info>,
#[account(
seeds = [b"vault".as_ref(), merchant.key().as_ref()],
bump = vault.bump,
)]
pub vault: Account<'info, Vault>,
#[account(mut)]
pub vault_token_account: Account<'info, TokenAccount>,
#[account(
init_if_needed,
payer = owner,
associated_token::mint = mint,
associated_token::authority = merchant
)]
pub merchant_token_account: Account<'info, TokenAccount>,
/// CHECK: Optional referrer
pub referrer: Option<AccountInfo<'info>>,
#[account(
init_if_needed,
payer = owner,
associated_token::mint = mint,
associated_token::authority = referrer.as_ref().map(|a| a.key()).unwrap_or_else(Pubkey::default)
)]
pub referrer_token_account: Option<Account<'info, TokenAccount>>,
#[account(
init_if_needed,
payer = owner,
associated_token::mint = mint,
associated_token::authority = fee_receiver
)]
pub fee_receiver_token_account: Account<'info, TokenAccount>,
pub merchant: SystemAccount<'info>,
pub fee_receiver: SystemAccount<'info>,
pub mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment