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
use crate::{
    error::ErrorCode,
    state::{MarketState, MINIMUM_BALANCE_FOR_SYSTEM_ACCS},
    utils::*,
    ClaimResource,
};
use anchor_lang::{prelude::*, solana_program::program_pack::Pack, system_program::System};
use anchor_spl::token;
use mpl_token_metadata::state::TokenMetadataAccount;

impl<'info> ClaimResource<'info> {
    pub fn process(&mut self, vault_owner_bump: u8) -> Result<()> {
        let market = &self.market;
        let selling_resource = &self.selling_resource;
        let vault = &self.vault;
        let metadata = &self.metadata;
        let vault_owner = &self.owner;
        let destination = &self.destination;
        let clock = &self.clock;
        let treasury_holder = &self.treasury_holder;
        let token_program = &self.token_program;

        // Check, that `Market` is `Ended`
        if let Some(end_date) = market.end_date {
            if clock.unix_timestamp as u64 <= end_date {
                return Err(ErrorCode::MarketInInvalidState.into());
            }
        } else if market.state != MarketState::Ended {
            return Err(ErrorCode::MarketInInvalidState.into());
        }

        let is_native = market.treasury_mint == System::id();

        let treasury_holder_amount = if is_native {
            treasury_holder
                .lamports()
                .checked_sub(MINIMUM_BALANCE_FOR_SYSTEM_ACCS)
                .ok_or(ErrorCode::MathOverflow)?
        } else {
            let token_account = spl_token::state::Account::unpack(&treasury_holder.data.borrow())?;
            if token_account.owner != market.treasury_owner {
                return Err(ErrorCode::DerivedKeyInvalid.into());
            }

            token_account.amount
        };

        // Check, that treasury balance is zero
        if treasury_holder_amount != 0 {
            return Err(ErrorCode::TreasuryIsNotEmpty.into());
        }

        // Check, that provided metadata is correct
        assert_derivation(
            &mpl_token_metadata::id(),
            metadata,
            &[
                mpl_token_metadata::state::PREFIX.as_bytes(),
                mpl_token_metadata::id().as_ref(),
                selling_resource.resource.as_ref(),
            ],
        )?;

        let signer_seeds: &[&[&[u8]]] = &[&[
            VAULT_OWNER_PREFIX.as_bytes(),
            selling_resource.resource.as_ref(),
            selling_resource.store.as_ref(),
            &[vault_owner_bump],
        ]];

        // Update primary sale flag
        let metadata_state: mpl_token_metadata::state::Metadata =
            mpl_token_metadata::state::Metadata::from_account_info(metadata)?;
        if !metadata_state.primary_sale_happened {
            mpl_update_primary_sale_happened_via_token(
                &metadata.to_account_info(),
                &vault_owner.to_account_info(),
                &vault.to_account_info(),
                signer_seeds[0],
            )?;
        }

        // Transfer token(ownership)
        let cpi_program = token_program.to_account_info();
        let cpi_accounts = token::Transfer {
            from: vault.to_account_info(),
            to: destination.to_account_info(),
            authority: vault_owner.to_account_info(),
        };
        let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);
        token::transfer(cpi_ctx, 1)?;

        Ok(())
    }
}