use {
super::utils::PERCENTAGE_BASE,
crate::{errors::*, state::*},
anchor_lang::{prelude::*, solana_program::system_program},
anchor_spl::token::{transfer, Token, TokenAccount, Transfer},
};
#[derive(Accounts)]
pub struct ProcessTask<'info> {
#[account(
mut,
seeds = [SEED_AUTHORITY],
bump = authority.bump,
owner = crate::ID,
)]
pub authority: Box<Account<'info, Authority>>,
pub clock: Sysvar<'info, Clock>,
#[account(
seeds = [SEED_CONFIG],
bump = config.bump,
owner = crate::ID,
)]
pub config: Account<'info, Config>,
#[account(
mut,
constraint = creditor_tokens.owner == payment.creditor,
constraint = creditor_tokens.mint == payment.mint,
owner = token_program.key(),
)]
pub creditor_tokens: Account<'info, TokenAccount>,
#[account(
mut,
constraint = debtor_tokens.owner == payment.debtor,
constraint = debtor_tokens.mint == payment.mint,
owner = token_program.key(),
)]
pub debtor_tokens: Account<'info, TokenAccount>,
#[account(
mut,
seeds = [
SEED_PAYMENT,
payment.debtor.key().as_ref(),
payment.id.to_be_bytes().as_ref(),
],
bump = payment.bump,
has_one = debtor_tokens,
has_one = creditor_tokens,
owner = crate::ID,
)]
pub payment: Account<'info, Payment>,
#[account(mut)]
pub signer: Signer<'info>,
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
#[account(
mut,
seeds = [
SEED_TASK,
payment.key().as_ref(),
task.process_at.to_string().as_bytes()
],
bump = task.bump,
owner = crate::ID,
has_one = payment,
constraint = task.status == TaskStatus::Pending @ ErrorCode::TaskNotPending,
constraint = task.process_at <= clock.unix_timestamp as u64 @ ErrorCode::TaskNotDue
)]
pub task: Account<'info, Task>,
#[account(
mut,
seeds = [SEED_TREASURY],
bump = treasury.bump,
owner = crate::ID,
)]
pub treasury: Account<'info, Treasury>,
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, Token>,
}
pub fn handler(ctx: Context<ProcessTask>) -> ProgramResult {
let authority = &ctx.accounts.authority;
let config = &ctx.accounts.config;
let creditor_tokens = &ctx.accounts.creditor_tokens;
let debtor_tokens = &ctx.accounts.debtor_tokens;
let payment = &mut ctx.accounts.payment;
let signer = &ctx.accounts.signer;
let task = &mut ctx.accounts.task;
let treasury = &ctx.accounts.treasury;
let token_program = &ctx.accounts.token_program;
let amount =
if payment.amount_percent > 0 { ((payment.amount_percent as f64) / (PERCENTAGE_BASE as f64) * (debtor_tokens.amount as f64)) as u64 }
else { payment.amount_raw };
if !debtor_tokens.delegate.is_some() || debtor_tokens.delegate.unwrap() != authority.key() {
task.transfer_status = TransferStatus::FailedNotAuthorized;
} else if debtor_tokens.delegated_amount < amount {
task.transfer_status = TransferStatus::FailedInsufficientDelegateBalance;
} else if debtor_tokens.amount < amount {
task.transfer_status = TransferStatus::FailedInsufficientBalance;
} else {
task.transfer_status = TransferStatus::Succeeded;
}
let next_transfer_at = task.process_at + payment.recurrence_interval;
if payment.recurrence_interval > 0 && next_transfer_at < payment.end_at {
task.status = TaskStatus::Repeat;
} else {
task.status = TaskStatus::Done;
}
if task.transfer_status == TransferStatus::Succeeded {
transfer(
CpiContext::new_with_signer(
token_program.to_account_info(),
Transfer {
authority: authority.to_account_info(),
from: debtor_tokens.to_account_info(),
to: creditor_tokens.to_account_info(),
},
&[&[SEED_AUTHORITY, &[authority.bump]]],
),
amount,
)?;
}
**payment.to_account_info().try_borrow_mut_lamports()? -= config.transfer_fee_distributor;
**signer.to_account_info().try_borrow_mut_lamports()? += config.transfer_fee_distributor;
**payment.to_account_info().try_borrow_mut_lamports()? -= config.transfer_fee_program;
**treasury.to_account_info().try_borrow_mut_lamports()? += config.transfer_fee_program;
return Ok(());
}