use {
crate::{errors::*, state::*, instructions::utils::*},
anchor_lang::{
prelude::*,
solana_program::{program::invoke, system_instruction, system_program},
},
anchor_spl::token::{approve, Approve, Mint, Token, TokenAccount},
index_program::state::*,
std::mem::size_of,
};
#[derive(Accounts)]
#[instruction(
memo: String,
amount_raw: u64,
amount_percent: u64,
recurrence_interval: u64,
start_at: u64,
end_at: u64,
creditor_payment_pointer_bump: u8,
creditor_payment_proof_bump: u8,
debtor_payment_pointer_bump: u8,
debtor_payment_proof_bump: u8,
payment_bump: u8,
task_bump: u8,
task_pointer_bump: u8,
task_proof_bump: u8,
)]
pub struct CreatePayment<'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: Box<Account<'info, Config>>,
pub creditor: AccountInfo<'info>,
#[account(
mut,
constraint = creditor_payment_index.owner == authority.key(),
constraint = creditor_payment_index.namespace == creditor_payment_namespace.key(),
owner = index_program.key(),
)]
pub creditor_payment_index: Box<Account<'info, Index>>,
#[account(
seeds = [
SEED_PAYMENT_NAMESPACE,
creditor.key().as_ref(),
Role::Creditor.to_string().as_bytes(),
],
bump = creditor_payment_namespace.bump,
owner = crate::ID,
)]
pub creditor_payment_namespace: Box<Account<'info, PaymentNamespace>>,
#[account(mut)]
pub creditor_payment_pointer: AccountInfo<'info>,
#[account(mut)]
pub creditor_payment_proof: AccountInfo<'info>,
#[account(
constraint = creditor_tokens.owner == creditor.key(),
constraint = creditor_tokens.mint == mint.key(),
owner = token_program.key(),
)]
pub creditor_tokens: Box<Account<'info, TokenAccount>>,
#[account(mut)]
pub debtor: Signer<'info>,
#[account(
mut,
constraint = debtor_payment_index.owner == authority.key(),
constraint = debtor_payment_index.namespace == debtor_payment_namespace.key(),
owner = index_program.key(),
)]
pub debtor_payment_index: Box<Account<'info, Index>>,
#[account(
seeds = [
SEED_PAYMENT_NAMESPACE,
debtor.key().as_ref(),
Role::Debtor.to_string().as_bytes(),
],
bump = debtor_payment_namespace.bump,
owner = crate::ID,
)]
pub debtor_payment_namespace: Box<Account<'info, PaymentNamespace>>,
#[account(mut)]
pub debtor_payment_pointer: AccountInfo<'info>,
#[account(mut)]
pub debtor_payment_proof: AccountInfo<'info>,
#[account(
mut,
constraint = debtor_tokens.owner == debtor.key(),
constraint = debtor_tokens.mint == mint.key(),
owner = token_program.key(),
)]
pub debtor_tokens: Box<Account<'info, TokenAccount>>,
#[account(address = index_program::ID)]
pub index_program: Program<'info, index_program::program::IndexProgram>,
#[account(owner = token_program.key())]
pub mint: Box<Account<'info, Mint>>,
#[account(
init,
seeds = [
SEED_PAYMENT,
debtor.key().as_ref(),
debtor_payment_index.clone().into_inner().count.to_be_bytes().as_ref()
],
bump = payment_bump,
payer = debtor,
space = 8 + size_of::<Payment>(),
)]
pub payment: Box<Account<'info, Payment>>,
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
#[account(
init,
seeds = [
SEED_TASK,
payment.key().as_ref(),
start_at.to_string().as_bytes()
],
bump = task_bump,
payer = debtor,
space = 8 + size_of::<Task>(),
)]
pub task: Box<Account<'info, Task>>,
#[account(
mut,
constraint = task_index.owner == authority.key(),
constraint = task_index.namespace == task_namespace.key(),
owner = index_program.key()
)]
pub task_index: Box<Account<'info, Index>>,
#[account(
seeds = [
SEED_TASK_NAMESPACE,
start_at.to_string().as_bytes()
],
bump = task_namespace.bump,
owner = crate::ID,
)]
pub task_namespace: Box<Account<'info, TaskNamespace>>,
#[account(mut)]
pub task_pointer: AccountInfo<'info>,
#[account(mut)]
pub task_proof: AccountInfo<'info>,
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, Token>,
}
pub fn handler(
ctx: Context<CreatePayment>,
memo: String,
amount_raw: u64,
amount_percent: u64,
recurrence_interval: u64,
start_at: u64,
end_at: u64,
creditor_payment_pointer_bump: u8,
creditor_payment_proof_bump: u8,
debtor_payment_pointer_bump: u8,
debtor_payment_proof_bump: u8,
payment_bump: u8,
task_bump: u8,
task_pointer_bump: u8,
task_proof_bump: u8,
) -> ProgramResult {
let authority = &ctx.accounts.authority;
let config = &ctx.accounts.config;
let creditor = &ctx.accounts.creditor;
let creditor_tokens = &ctx.accounts.creditor_tokens;
let creditor_payment_index = &ctx.accounts.creditor_payment_index;
let creditor_payment_pointer = &ctx.accounts.creditor_payment_pointer;
let creditor_payment_proof = &ctx.accounts.creditor_payment_proof;
let debtor = &mut ctx.accounts.debtor;
let debtor_payment_index = &ctx.accounts.debtor_payment_index;
let debtor_payment_pointer = &ctx.accounts.debtor_payment_pointer;
let debtor_payment_proof = &ctx.accounts.debtor_payment_proof;
let debtor_tokens = &mut ctx.accounts.debtor_tokens;
let index_program = &ctx.accounts.index_program;
let mint = &ctx.accounts.mint;
let payment = &mut ctx.accounts.payment;
let system_program = &ctx.accounts.system_program;
let task = &mut ctx.accounts.task;
let task_index = &ctx.accounts.task_index;
let task_pointer = &ctx.accounts.task_pointer;
let task_proof = &ctx.accounts.task_proof;
let token_program = &ctx.accounts.token_program;
if amount_raw > 0 {
require!(amount_percent == 0, ErrorCode::Unknown); } else if amount_percent > 0 {
require!(amount_raw == 0, ErrorCode::Unknown); require!(amount_percent <= PERCENTAGE_BASE, ErrorCode::Unknown);
} else {
return Err(ErrorCode::Unknown.into()); }
match recurrence_interval {
0 => require!(start_at == end_at, ErrorCode::InvalidChronology), _ => require!(start_at <= end_at, ErrorCode::InvalidChronology) }
require!(
recurrence_interval % ONE_MINUTE == 0,
ErrorCode::InvalidRecurrenceInterval
);
let num_transfers = match recurrence_interval {
0 => 1, _ => (end_at - start_at) / recurrence_interval, };
let transfer_fee = num_transfers * (
config.transfer_fee_distributor +
config.transfer_fee_program
);
require!(
debtor.to_account_info().lamports() >= transfer_fee,
ErrorCode::InsufficientBalance
);
payment.id = debtor_payment_index.count;
payment.amount_raw = amount_raw;
payment.amount_percent = amount_percent;
payment.debtor = debtor.key();
payment.debtor_tokens = debtor_tokens.key();
payment.creditor = creditor.key();
payment.creditor_tokens = creditor_tokens.key();
payment.memo = memo;
payment.mint = mint.key();
payment.recurrence_interval = recurrence_interval;
payment.start_at = start_at;
payment.end_at = end_at;
payment.bump = payment_bump;
task.payment = payment.key();
task.status = TaskStatus::Pending;
task.transfer_status = TransferStatus::Pending;
task.process_at = start_at;
task.bump = task_bump;
approve(
CpiContext::new(
token_program.to_account_info(),
Approve {
authority: debtor.to_account_info(),
delegate: authority.to_account_info(),
to: debtor_tokens.to_account_info(),
},
),
u64::MAX,
)?;
invoke(
&system_instruction::transfer(
&debtor.key(),
&payment.key(),
transfer_fee
),
&[
debtor.to_account_info().clone(),
payment.to_account_info().clone(),
system_program.to_account_info().clone(),
],
)?;
index_program::cpi::create_pointer(
CpiContext::new_with_signer(
index_program.to_account_info(),
index_program::cpi::accounts::CreatePointer {
index: creditor_payment_index.to_account_info(),
pointer: creditor_payment_pointer.to_account_info(),
proof: creditor_payment_proof.to_account_info(),
owner: authority.to_account_info(),
payer: debtor.to_account_info(),
system_program: system_program.to_account_info(),
},
&[&[SEED_AUTHORITY, &[authority.bump]]]
),
creditor_payment_index.count.to_string(),
payment.key(),
creditor_payment_pointer_bump,
creditor_payment_proof_bump
)?;
index_program::cpi::create_pointer(
CpiContext::new_with_signer(
index_program.to_account_info(),
index_program::cpi::accounts::CreatePointer {
index: debtor_payment_index.to_account_info(),
pointer: debtor_payment_pointer.to_account_info(),
proof: debtor_payment_proof.to_account_info(),
owner: authority.to_account_info(),
payer: debtor.to_account_info(),
system_program: system_program.to_account_info(),
},
&[&[SEED_AUTHORITY, &[authority.bump]]]
),
debtor_payment_index.count.to_string(),
payment.key(),
debtor_payment_pointer_bump,
debtor_payment_proof_bump
)?;
index_program::cpi::create_pointer(
CpiContext::new_with_signer(
index_program.to_account_info(),
index_program::cpi::accounts::CreatePointer {
index: task_index.to_account_info(),
pointer: task_pointer.to_account_info(),
proof: task_proof.to_account_info(),
owner: authority.to_account_info(),
payer: debtor.to_account_info(),
system_program: system_program.to_account_info(),
},
&[&[SEED_AUTHORITY, &[authority.bump]]]
),
task_index.count.to_string(),
task.key(),
task_pointer_bump,
task_proof_bump
)?;
return Ok(());
}