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
//! Edit metadata
use bonfida_utils::{BorshSize, InstructionsAccount};
use borsh::{BorshDeserialize, BorshSerialize};
use mpl_token_metadata::{
    instruction::update_metadata_accounts_v2, pda::find_metadata_account, state::DataV2,
};
use solana_program::{
    account_info::{AccountInfo, next_account_info},
    entrypoint::ProgramResult,
    program::invoke_signed,
    program_error::ProgramError,
    pubkey::Pubkey,
};

use crate::{error::AccessError};
use crate::instruction::ProgramInstruction::EditMetadata;
use crate::state::V1_INSTRUCTIONS_ALLOWED;
use crate::utils::{check_account_key, check_account_owner, check_signer};
use crate::state:: CentralStateV2;

#[derive(BorshDeserialize, BorshSerialize, BorshSize)]
/// The required parameters for the `edit_metadata` instruction
pub struct Params {
    // The name of the token
    pub name: String,
    // The symbol of the token
    pub symbol: String,
    // The URI of the token logo
    pub uri: String,
}

#[derive(InstructionsAccount)]
/// The required accounts for the `change_inflation` instruction
pub struct Accounts<'a, T> {
    /// The central state account
    pub central_state: &'a T,

    /// The central state account authority
    #[cons(signer)]
    pub authority: &'a T,

    /// The metadata account
    #[cons(writable)]
    pub metadata: &'a T,

    /// The metadata program account
    pub metadata_program: &'a T,
}

impl<'a, 'b: 'a> Accounts<'a, AccountInfo<'b>> {
    pub fn parse(
        accounts: &'a [AccountInfo<'b>],
        program_id: &Pubkey,
    ) -> Result<Self, ProgramError> {
        let accounts_iter = &mut accounts.iter();
        let accounts = Accounts {
            central_state: next_account_info(accounts_iter)?,
            authority: next_account_info(accounts_iter)?,
            metadata: next_account_info(accounts_iter)?,
            metadata_program: next_account_info(accounts_iter)?,
        };

        // Check keys
        check_account_key(
            accounts.metadata_program,
            &mpl_token_metadata::ID,
            AccessError::WrongMplProgram,
        )?;

        // Check ownership
        check_account_owner(accounts.central_state, program_id, AccessError::WrongOwner)?;
        check_account_owner(
            accounts.metadata,
            &mpl_token_metadata::ID,
            AccessError::WrongOwner,
        )?;

        // Check signer
        check_signer(
            accounts.authority,
            AccessError::CentralStateAuthorityMustSign,
        )?;

        Ok(accounts)
    }
}

pub fn process_edit_metadata(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    params: Params,
) -> ProgramResult {
    if !V1_INSTRUCTIONS_ALLOWED {
        return Err(AccessError::DeprecatedInstruction.into());
    }

    let accounts = Accounts::parse(accounts, program_id)?;

    let central_state = CentralStateV2::from_account_info(accounts.central_state)?;
    central_state.assert_instruction_allowed(&EditMetadata)?;
    let (metadata_key, _) = find_metadata_account(&central_state.token_mint);

    check_account_key(
        accounts.authority,
        &central_state.authority,
        AccessError::WrongCentralStateAuthority,
    )?;
    check_account_key(
        accounts.metadata,
        &metadata_key,
        AccessError::AccountNotDeterministic,
    )?;

    let data = DataV2 {
        name: params.name,
        uri: params.uri,
        symbol: params.symbol,
        seller_fee_basis_points: 0,
        creators: None,
        collection: None,
        uses: None,
    };

    let ix = update_metadata_accounts_v2(
        *accounts.metadata_program.key,
        *accounts.metadata.key,
        *accounts.central_state.key,
        None,
        Some(data),
        None,
        Some(true),
    );
    invoke_signed(
        &ix,
        &[accounts.metadata.clone(), accounts.central_state.clone()],
        &[&[&program_id.to_bytes(), &[central_state.bump_seed]]],
    )?;

    Ok(())
}