defios 0.1.0

Main github on-chain integrations by DefiOS
Documentation
use crate::{
    error::DefiOSError,
    event::IssueUnstaked,
    state::{Issue, IssueStaker, Repository},
};
use anchor_lang::prelude::*;
use anchor_spl::token::{
    close_account, transfer, CloseAccount, Mint, Token, TokenAccount, Transfer,
};

#[derive(Accounts)]
pub struct UnstakeIssue<'info> {
    #[account(mut)]
    pub issue_staker: Signer<'info>,

    #[account(
        mut,
        constraint = issue_staker_token_account.mint.eq(&issue_token_pool_account.mint),
        constraint = issue_staker_token_account.owner.eq(&issue_staker.key()),
    )]
    pub issue_staker_token_account: Account<'info, TokenAccount>,

    #[account(
        mut,
        address = issue_account.repository,
        seeds = [
            b"repository",
            repository_account.id.as_bytes(),
            repository_account.repository_creator.key().as_ref(),
        ],
        bump = repository_account.bump
    )]
    pub repository_account: Box<Account<'info, Repository>>,

    #[account(
        mut,
        seeds = [
            b"issue",
            issue_account.index.to_string().as_bytes(),
            repository_account.key().as_ref(),
            issue_account.issue_creator.key().as_ref(),
        ],
        bump = issue_account.bump
    )]
    pub issue_account: Account<'info, Issue>,

    #[account(mut)]
    pub issue_token_pool_account: Account<'info, TokenAccount>,

    #[account(
        mut,
        close = issue_staker,
        seeds = [
            b"issuestaker",
            issue_account.key().as_ref(),
            issue_staker.key().as_ref(),
        ],
        bump = issue_staker_account.bump
    )]
    pub issue_staker_account: Account<'info, IssueStaker>,

    #[account(
        constraint = rewards_mint.key()==issue_account.issue_token,
        constraint = rewards_mint.key().eq(&issue_token_pool_account.mint)
    )]
    pub rewards_mint: Account<'info, Mint>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
}

pub fn handler(ctx: Context<UnstakeIssue>) -> Result<()> {
    let issue_staker = &ctx.accounts.issue_staker;
    let issue_account = &mut ctx.accounts.issue_account;
    let repository_account = &ctx.accounts.repository_account;
    let issue_staker_account = &mut ctx.accounts.issue_staker_account;
    let issue_staker_token_account = &ctx.accounts.issue_staker_token_account;
    let issue_token_pool_account = &ctx.accounts.issue_token_pool_account;
    let rewards_mint = &ctx.accounts.rewards_mint;

    require!(
        issue_account.closed_at.is_none(),
        DefiOSError::IssueClosedAlready
    );

    let issue_index_str = issue_account.index.to_string();
    let repository_account_key = repository_account.key();
    let issue_creator_key = issue_account.issue_creator.key();

    require!(
        issue_staker_account.staked_amount != 0,
        DefiOSError::CantUnnstakeBeforeStaking
    );

    require!(
        issue_staker_account.has_voted == false,
        DefiOSError::CantUnstakeAfterVoting
    );

    let signer_seeds: &[&[&[u8]]] = &[&[
        b"issue",
        issue_index_str.as_bytes(),
        repository_account_key.as_ref(),
        issue_creator_key.as_ref(),
        &[issue_account.bump],
    ]];

    transfer(
        CpiContext::new_with_signer(
            ctx.accounts.token_program.to_account_info(),
            Transfer {
                from: issue_token_pool_account.to_account_info(),
                to: issue_staker_token_account.to_account_info(),
                authority: issue_account.to_account_info(),
            },
            signer_seeds,
        ),
        issue_staker_account.staked_amount,
    )?;

    close_account(CpiContext::new_with_signer(
        ctx.accounts.token_program.to_account_info(),
        CloseAccount {
            account: issue_token_pool_account.to_account_info(),
            authority: issue_account.to_account_info(),
            destination: issue_staker.to_account_info(),
        },
        signer_seeds,
    ))?;

    emit!(IssueUnstaked {
        issue_account: issue_account.key(),
        issue_staker: issue_staker.key(),
        issue_staker_token_account: issue_staker_token_account.key(),
        rewards_mint: rewards_mint.key(),
        unstaked_amount: issue_staker_account.staked_amount,
        issue_contribution_link: issue_account.uri.clone()
    });

    issue_account.total_stake_amount -= issue_staker_account.staked_amount;
    issue_staker_account.staked_amount = 0;

    Ok(())
}