Created
August 28, 2025 16:03
-
-
Save yuhcee/7c4385c574d8c4fd7e39fd80b1ed3f46 to your computer and use it in GitHub Desktop.
An Anchor PDA/CPI vault program for deposits and settlements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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