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
//! Inlined MPL metadata types to avoid a direct dependency on `mpl-token-metadata'
//! NOTE: this file is sym-linked in `spl-single-pool`, so be careful
//! with changes!

solana_program::declare_id!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");

pub(crate) mod instruction {
    use {
        super::state::DataV2,
        borsh::{BorshDeserialize, BorshSerialize},
        solana_program::{
            instruction::{AccountMeta, Instruction},
            pubkey::Pubkey,
        },
    };

    #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
    struct CreateMetadataAccountArgsV3 {
        /// Note that unique metadatas are disabled for now.
        pub data: DataV2,
        /// Whether you want your metadata to be updateable in the future.
        pub is_mutable: bool,
        /// UNUSED If this is a collection parent NFT.
        pub collection_details: Option<u8>,
    }

    #[allow(clippy::too_many_arguments)]
    pub(crate) fn create_metadata_accounts_v3(
        program_id: Pubkey,
        metadata_account: Pubkey,
        mint: Pubkey,
        mint_authority: Pubkey,
        payer: Pubkey,
        update_authority: Pubkey,
        name: String,
        symbol: String,
        uri: String,
    ) -> Instruction {
        let mut data = vec![33]; // CreateMetadataAccountV3
        data.append(
            &mut CreateMetadataAccountArgsV3 {
                data: DataV2 {
                    name,
                    symbol,
                    uri,
                    seller_fee_basis_points: 0,
                    creators: None,
                    collection: None,
                    uses: None,
                },
                is_mutable: true,
                collection_details: None,
            }
            .try_to_vec()
            .unwrap(),
        );
        Instruction {
            program_id,
            accounts: vec![
                AccountMeta::new(metadata_account, false),
                AccountMeta::new_readonly(mint, false),
                AccountMeta::new_readonly(mint_authority, true),
                AccountMeta::new(payer, true),
                AccountMeta::new_readonly(update_authority, true),
                AccountMeta::new_readonly(solana_program::system_program::ID, false),
            ],
            data,
        }
    }

    #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
    struct UpdateMetadataAccountArgsV2 {
        pub data: Option<DataV2>,
        pub update_authority: Option<Pubkey>,
        pub primary_sale_happened: Option<bool>,
        pub is_mutable: Option<bool>,
    }
    pub(crate) fn update_metadata_accounts_v2(
        program_id: Pubkey,
        metadata_account: Pubkey,
        update_authority: Pubkey,
        new_update_authority: Option<Pubkey>,
        metadata: Option<DataV2>,
        primary_sale_happened: Option<bool>,
        is_mutable: Option<bool>,
    ) -> Instruction {
        let mut data = vec![15]; // UpdateMetadataAccountV2
        data.append(
            &mut UpdateMetadataAccountArgsV2 {
                data: metadata,
                update_authority: new_update_authority,
                primary_sale_happened,
                is_mutable,
            }
            .try_to_vec()
            .unwrap(),
        );
        Instruction {
            program_id,
            accounts: vec![
                AccountMeta::new(metadata_account, false),
                AccountMeta::new_readonly(update_authority, true),
            ],
            data,
        }
    }
}

/// PDA creation helpers
pub mod pda {
    use {super::ID, solana_program::pubkey::Pubkey};
    const PREFIX: &str = "metadata";
    /// Helper to find a metadata account address
    pub fn find_metadata_account(mint: &Pubkey) -> (Pubkey, u8) {
        Pubkey::find_program_address(&[PREFIX.as_bytes(), ID.as_ref(), mint.as_ref()], &ID)
    }
}

pub(crate) mod state {
    use borsh::{BorshDeserialize, BorshSerialize};
    #[repr(C)]
    #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
    pub(crate) struct DataV2 {
        /// The name of the asset
        pub name: String,
        /// The symbol for the asset
        pub symbol: String,
        /// URI pointing to JSON representing the asset
        pub uri: String,
        /// Royalty basis points that goes to creators in secondary sales (0-10000)
        pub seller_fee_basis_points: u16,
        /// UNUSED Array of creators, optional
        pub creators: Option<u8>,
        /// UNUSED Collection
        pub collection: Option<u8>,
        /// UNUSED Uses
        pub uses: Option<u8>,
    }
}