Spaces:
Sleeping
Sleeping
File size: 5,352 Bytes
d2948d0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | #![allow(unexpected_cfgs)]
#![allow(clippy::diverging_sub_expression)]
use anchor_lang::prelude::*;
use anchor_lang::system_program::{transfer, Transfer};
declare_id!("EpB6hUZUf1vvvTVAYvEN57pjUWfYswaAuKGGQDHP5iH");
#[program]
pub mod cardcli_fee_vault {
use super::*;
pub fn initialize(
ctx: Context<Initialize>,
fee_bps: u16,
fixed_fee_usd_cents: u16,
) -> Result<()> {
require!(fee_bps <= 10_000, FeeVaultError::InvalidFeeBasisPoints);
let vault = &mut ctx.accounts.fee_vault;
vault.authority = ctx.accounts.authority.key();
vault.fee_bps = fee_bps;
vault.fixed_fee_usd_cents = fixed_fee_usd_cents;
vault.total_lamports_collected = 0;
vault.payment_count = 0;
vault.bump = ctx.bumps.fee_vault;
vault.created_at = Clock::get()?.unix_timestamp;
Ok(())
}
pub fn update_pricing(
ctx: Context<UpdatePricing>,
fee_bps: u16,
fixed_fee_usd_cents: u16,
) -> Result<()> {
require!(fee_bps <= 10_000, FeeVaultError::InvalidFeeBasisPoints);
let vault = &mut ctx.accounts.fee_vault;
require_keys_eq!(
vault.authority,
ctx.accounts.authority.key(),
FeeVaultError::Unauthorized
);
vault.fee_bps = fee_bps;
vault.fixed_fee_usd_cents = fixed_fee_usd_cents;
Ok(())
}
pub fn collect_fee(
ctx: Context<CollectFee>,
amount_lamports: u64,
fee_reference: [u8; 16],
) -> Result<()> {
require!(amount_lamports > 0, FeeVaultError::InvalidFeeAmount);
let cpi_accounts = Transfer {
from: ctx.accounts.payer.to_account_info(),
to: ctx.accounts.fee_vault.to_account_info(),
};
let cpi_ctx = CpiContext::new(ctx.accounts.system_program.to_account_info(), cpi_accounts);
transfer(cpi_ctx, amount_lamports)?;
let vault = &mut ctx.accounts.fee_vault;
vault.total_lamports_collected = vault
.total_lamports_collected
.checked_add(amount_lamports)
.ok_or(FeeVaultError::MathOverflow)?;
vault.payment_count = vault
.payment_count
.checked_add(1)
.ok_or(FeeVaultError::MathOverflow)?;
emit!(FeeCollected {
payer: ctx.accounts.payer.key(),
amount_lamports,
fee_reference,
payment_count: vault.payment_count,
});
Ok(())
}
pub fn withdraw(ctx: Context<Withdraw>, lamports: u64) -> Result<()> {
let vault = &ctx.accounts.fee_vault;
require_keys_eq!(
vault.authority,
ctx.accounts.authority.key(),
FeeVaultError::Unauthorized
);
require!(lamports > 0, FeeVaultError::InvalidFeeAmount);
let vault_info = ctx.accounts.fee_vault.to_account_info();
let recipient_info = ctx.accounts.recipient.to_account_info();
let vault_balance = vault_info.lamports();
require!(
vault_balance >= lamports,
FeeVaultError::InsufficientVaultBalance
);
**vault_info.try_borrow_mut_lamports()? -= lamports;
**recipient_info.try_borrow_mut_lamports()? += lamports;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(
init,
payer = authority,
space = 8 + FeeVault::LEN,
seeds = [b"fee_vault"],
bump,
)]
pub fee_vault: Account<'info, FeeVault>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct UpdatePricing<'info> {
#[account(mut, seeds = [b"fee_vault"], bump = fee_vault.bump)]
pub fee_vault: Account<'info, FeeVault>,
pub authority: Signer<'info>,
}
#[derive(Accounts)]
pub struct CollectFee<'info> {
#[account(mut, seeds = [b"fee_vault"], bump = fee_vault.bump)]
pub fee_vault: Account<'info, FeeVault>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut, seeds = [b"fee_vault"], bump = fee_vault.bump)]
pub fee_vault: Account<'info, FeeVault>,
pub authority: Signer<'info>,
#[account(mut)]
pub recipient: SystemAccount<'info>,
}
#[account]
pub struct FeeVault {
pub authority: Pubkey,
pub fee_bps: u16,
pub fixed_fee_usd_cents: u16,
pub total_lamports_collected: u64,
pub payment_count: u64,
pub bump: u8,
pub created_at: i64,
}
impl FeeVault {
pub const LEN: usize = 32 + 2 + 2 + 8 + 8 + 1 + 8;
}
#[event]
pub struct FeeCollected {
pub payer: Pubkey,
pub amount_lamports: u64,
pub fee_reference: [u8; 16],
pub payment_count: u64,
}
#[error_code]
pub enum FeeVaultError {
#[msg("Fee basis points must be between 0 and 10_000")]
InvalidFeeBasisPoints,
#[msg("Fee amount must be greater than zero")]
InvalidFeeAmount,
#[msg("Math overflow")]
MathOverflow,
#[msg("Unauthorized authority")]
Unauthorized,
#[msg("Vault balance is insufficient")]
InsufficientVaultBalance,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fee_vault_length_stays_stable() {
assert_eq!(FeeVault::LEN, 61);
}
}
|