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
use borsh::BorshSerialize;
use mpl_utils::{assert_signer, create_or_allocate_account_raw};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint::ProgramResult,
    program_memory::sol_memcpy,
    pubkey::Pubkey,
    system_program,
};

use super::find_escrow_seeds;
use crate::{
    assertions::{assert_derivation, assert_initialized, assert_owned_by},
    error::MetadataError,
    pda::{EDITION, PREFIX},
    state::{
        EscrowAuthority, Key, Metadata, TokenMetadataAccount, TokenOwnedEscrow, TokenStandard,
    },
    utils::check_token_standard,
};

pub fn process_create_escrow_account(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    let account_info_iter = &mut accounts.iter();

    let escrow_account_info = next_account_info(account_info_iter)?;
    if escrow_account_info.owner != &system_program::ID || !escrow_account_info.data_is_empty() {
        return Err(MetadataError::AlreadyInitialized.into());
    }

    let metadata_account_info = next_account_info(account_info_iter)?;
    assert_owned_by(metadata_account_info, &crate::ID)?;

    let mint_account_info = next_account_info(account_info_iter)?;
    assert_owned_by(mint_account_info, &spl_token::id())?;

    let token_account_info = next_account_info(account_info_iter)?;
    assert_owned_by(token_account_info, &spl_token::id())?;

    let edition_account_info = next_account_info(account_info_iter)?;
    assert_owned_by(edition_account_info, &crate::ID)?;

    let payer_account_info = next_account_info(account_info_iter)?;
    assert_signer(payer_account_info)?;

    let system_account_info = next_account_info(account_info_iter)?;
    if *system_account_info.key != system_program::id() {
        return Err(MetadataError::InvalidSystemProgram.into());
    }

    let sysvar_ix_account_info = next_account_info(account_info_iter)?;
    if sysvar_ix_account_info.key != &solana_program::sysvar::instructions::ID {
        return Err(MetadataError::InvalidInstructionsSysvar.into());
    }

    let is_using_authority = account_info_iter.len() == 1;

    let maybe_authority_info: Option<&AccountInfo> = if is_using_authority {
        Some(next_account_info(account_info_iter)?)
    } else {
        None
    };

    let metadata: Metadata = Metadata::from_account_info(metadata_account_info)?;

    // Mint account passed in must be the mint of the metadata account passed in.
    if &metadata.mint != mint_account_info.key {
        return Err(MetadataError::MintMismatch.into());
    }

    // Only non-fungible tokens (i.e. unique) can have escrow accounts.
    if check_token_standard(mint_account_info, Some(edition_account_info))?
        != TokenStandard::NonFungible
    {
        return Err(MetadataError::MustBeNonFungible.into());
    };

    // Check that the edition account is for this mint.
    let _edition_bump = assert_derivation(
        &crate::ID,
        edition_account_info,
        &[
            PREFIX.as_bytes(),
            crate::id().as_ref(),
            mint_account_info.key.as_ref(),
            EDITION.as_bytes(),
        ],
    )?;

    let creator = maybe_authority_info.unwrap_or(payer_account_info);
    assert_signer(creator)?;

    let token_account: spl_token::state::Account = assert_initialized(token_account_info)?;

    if token_account.mint != *mint_account_info.key {
        return Err(MetadataError::MintMismatch.into());
    }

    if token_account.amount < 1 {
        return Err(MetadataError::NotEnoughTokens.into());
    }

    if token_account.mint != metadata.mint {
        return Err(MetadataError::MintMismatch.into());
    }

    let creator_type = if token_account.owner == *creator.key {
        EscrowAuthority::TokenOwner
    } else {
        EscrowAuthority::Creator(*creator.key)
    };

    // Derive the seeds for PDA signing.
    let escrow_seeds = find_escrow_seeds(mint_account_info.key, &creator_type);

    let bump_seed = &[assert_derivation(
        &crate::ID,
        escrow_account_info,
        &escrow_seeds,
    )?];

    let escrow_authority_seeds = [escrow_seeds, vec![bump_seed]].concat();

    // Initialize a default (empty) escrow structure.
    let toe = TokenOwnedEscrow {
        key: Key::TokenOwnedEscrow,
        base_token: *mint_account_info.key,
        authority: creator_type,
        bump: bump_seed[0],
    };

    let serialized_data = toe
        .try_to_vec()
        .map_err(|_| MetadataError::BorshSerializationError)?;

    // Create the account.
    create_or_allocate_account_raw(
        crate::ID,
        escrow_account_info,
        system_account_info,
        payer_account_info,
        serialized_data.len(),
        &escrow_authority_seeds,
    )?;

    sol_memcpy(
        &mut escrow_account_info.try_borrow_mut_data()?,
        &serialized_data,
        serialized_data.len(),
    );

    Ok(())
}