Skip to main content

mpl_token_metadata/instruction/
metadata.rs

1use borsh::{BorshDeserialize, BorshSerialize};
2use solana_program::{
3    instruction::{AccountMeta, Instruction},
4    pubkey::Pubkey,
5};
6#[cfg(feature = "serde-feature")]
7use {
8    serde::{Deserialize, Serialize},
9    serde_with::{As, DisplayFromStr},
10};
11
12use super::InstructionBuilder;
13use crate::{
14    instruction::MetadataInstruction,
15    processor::AuthorizationData,
16    state::{
17        AssetData, Collection, CollectionDetails, Creator, Data, DataV2, MigrationType,
18        PrintSupply, TokenStandard, Uses,
19    },
20};
21
22//----------------------+
23// Instruction args     |
24//----------------------+
25
26#[repr(C)]
27#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
28#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
29/// Args for create call
30pub struct CreateMetadataAccountArgsV3 {
31    /// Note that unique metadatas are disabled for now.
32    pub data: DataV2,
33    /// Whether you want your metadata to be updateable in the future.
34    pub is_mutable: bool,
35    /// If this is a collection parent NFT.
36    pub collection_details: Option<CollectionDetails>,
37}
38
39#[repr(C)]
40#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
41#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
42pub enum CreateArgs {
43    V1 {
44        asset_data: AssetData,
45        decimals: Option<u8>,
46        print_supply: Option<PrintSupply>,
47    },
48}
49
50#[repr(C)]
51#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
52#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
53pub enum MintArgs {
54    V1 {
55        amount: u64,
56        /// Required authorization data to validate the request.
57        authorization_data: Option<AuthorizationData>,
58    },
59}
60
61#[repr(C)]
62#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
63#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
64pub enum TransferArgs {
65    V1 {
66        amount: u64,
67        /// Required authorization data to validate the request.
68        authorization_data: Option<AuthorizationData>,
69    },
70}
71
72/// Struct representing the values to be updated for an `update` instructions.
73///
74/// Values that are set to `None` are not changed.  Any value set to `Some(...)` will
75/// have its value updated. There are properties that have three valid states, and
76/// use a "toggle" type that allows the value to be set, cleared, or remain the same.
77#[repr(C)]
78#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
79#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
80pub enum UpdateArgs {
81    V1 {
82        /// The new update authority.
83        new_update_authority: Option<Pubkey>,
84        /// The metadata details.
85        data: Option<Data>,
86        /// Indicates whether the primary sale has happened or not (once set to `true`, it cannot be
87        /// changed back).
88        primary_sale_happened: Option<bool>,
89        // Indicates Whether the data struct is mutable or not (once set to `true`, it cannot be
90        /// changed back).
91        is_mutable: Option<bool>,
92        /// Collection information.
93        collection: CollectionToggle,
94        /// Additional details of the collection.
95        collection_details: CollectionDetailsToggle,
96        /// Uses information.
97        uses: UsesToggle,
98        // Programmable rule set configuration (only applicable to `Programmable` asset types).
99        rule_set: RuleSetToggle,
100        /// Required authorization data to validate the request.
101        authorization_data: Option<AuthorizationData>,
102    },
103    AsUpdateAuthorityV2 {
104        /// The new update authority.
105        new_update_authority: Option<Pubkey>,
106        /// The metadata details.
107        data: Option<Data>,
108        /// Indicates whether the primary sale has happened or not (once set to `true`, it cannot be
109        /// changed back).
110        primary_sale_happened: Option<bool>,
111        // Indicates Whether the data struct is mutable or not (once set to `true`, it cannot be
112        /// changed back).
113        is_mutable: Option<bool>,
114        /// Collection information.
115        collection: CollectionToggle,
116        /// Additional details of the collection.
117        collection_details: CollectionDetailsToggle,
118        /// Uses information.
119        uses: UsesToggle,
120        // Programmable rule set configuration (only applicable to `Programmable` asset types).
121        rule_set: RuleSetToggle,
122        /// Token standard.
123        token_standard: Option<TokenStandard>,
124        /// Required authorization data to validate the request.
125        authorization_data: Option<AuthorizationData>,
126    },
127    AsAuthorityItemDelegateV2 {
128        /// The new update authority.
129        new_update_authority: Option<Pubkey>,
130        /// Indicates whether the primary sale has happened or not (once set to `true`, it cannot be
131        /// changed back).
132        primary_sale_happened: Option<bool>,
133        // Indicates Whether the data struct is mutable or not (once set to `true`, it cannot be
134        /// changed back).
135        is_mutable: Option<bool>,
136        /// Token standard.
137        token_standard: Option<TokenStandard>,
138        /// Required authorization data to validate the request.
139        authorization_data: Option<AuthorizationData>,
140    },
141    AsCollectionDelegateV2 {
142        /// Collection information.
143        collection: CollectionToggle,
144        /// Required authorization data to validate the request.
145        authorization_data: Option<AuthorizationData>,
146    },
147    AsDataDelegateV2 {
148        /// The metadata details.
149        data: Option<Data>,
150        /// Required authorization data to validate the request.
151        authorization_data: Option<AuthorizationData>,
152    },
153    AsProgrammableConfigDelegateV2 {
154        // Programmable rule set configuration (only applicable to `Programmable` asset types).
155        rule_set: RuleSetToggle,
156        /// Required authorization data to validate the request.
157        authorization_data: Option<AuthorizationData>,
158    },
159    AsDataItemDelegateV2 {
160        /// The metadata details.
161        data: Option<Data>,
162        /// Required authorization data to validate the request.
163        authorization_data: Option<AuthorizationData>,
164    },
165    AsCollectionItemDelegateV2 {
166        /// Collection information.
167        collection: CollectionToggle,
168        /// Required authorization data to validate the request.
169        authorization_data: Option<AuthorizationData>,
170    },
171    AsProgrammableConfigItemDelegateV2 {
172        // Programmable rule set configuration (only applicable to `Programmable` asset types).
173        rule_set: RuleSetToggle,
174        /// Required authorization data to validate the request.
175        authorization_data: Option<AuthorizationData>,
176    },
177}
178
179impl UpdateArgs {
180    pub fn default_v1() -> Self {
181        Self::V1 {
182            new_update_authority: None,
183            data: None,
184            primary_sale_happened: None,
185            is_mutable: None,
186            collection: CollectionToggle::default(),
187            collection_details: CollectionDetailsToggle::default(),
188            uses: UsesToggle::default(),
189            rule_set: RuleSetToggle::default(),
190            authorization_data: None,
191        }
192    }
193
194    pub fn default_as_update_authority() -> Self {
195        Self::AsUpdateAuthorityV2 {
196            new_update_authority: None,
197            data: None,
198            primary_sale_happened: None,
199            is_mutable: None,
200            collection: CollectionToggle::default(),
201            collection_details: CollectionDetailsToggle::default(),
202            uses: UsesToggle::default(),
203            rule_set: RuleSetToggle::default(),
204            token_standard: None,
205            authorization_data: None,
206        }
207    }
208
209    pub fn default_as_authority_item_delegate() -> Self {
210        Self::AsAuthorityItemDelegateV2 {
211            new_update_authority: None,
212            primary_sale_happened: None,
213            is_mutable: None,
214            token_standard: None,
215            authorization_data: None,
216        }
217    }
218
219    pub fn default_as_collection_delegate() -> Self {
220        Self::AsCollectionDelegateV2 {
221            collection: CollectionToggle::default(),
222            authorization_data: None,
223        }
224    }
225
226    pub fn default_as_data_delegate() -> Self {
227        Self::AsDataDelegateV2 {
228            data: None,
229            authorization_data: None,
230        }
231    }
232
233    pub fn default_as_programmable_config_delegate() -> Self {
234        Self::AsProgrammableConfigDelegateV2 {
235            rule_set: RuleSetToggle::default(),
236            authorization_data: None,
237        }
238    }
239
240    pub fn default_as_data_item_delegate() -> Self {
241        Self::AsDataItemDelegateV2 {
242            data: None,
243            authorization_data: None,
244        }
245    }
246
247    pub fn default_as_collection_item_delegate() -> Self {
248        Self::AsCollectionItemDelegateV2 {
249            collection: CollectionToggle::default(),
250            authorization_data: None,
251        }
252    }
253
254    pub fn default_as_programmable_config_item_delegate() -> Self {
255        Self::AsProgrammableConfigItemDelegateV2 {
256            rule_set: RuleSetToggle::default(),
257            authorization_data: None,
258        }
259    }
260}
261
262//-- Toggle implementations
263
264#[repr(C)]
265#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
266#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Default)]
267pub enum CollectionToggle {
268    #[default]
269    None,
270    Clear,
271    Set(Collection),
272}
273
274impl CollectionToggle {
275    pub fn is_some(&self) -> bool {
276        matches!(self, CollectionToggle::Clear | CollectionToggle::Set(_))
277    }
278
279    pub fn is_none(&self) -> bool {
280        matches!(self, CollectionToggle::None)
281    }
282
283    pub fn is_clear(&self) -> bool {
284        matches!(self, CollectionToggle::Clear)
285    }
286
287    pub fn is_set(&self) -> bool {
288        matches!(self, CollectionToggle::Set(_))
289    }
290
291    pub fn to_option(self) -> Option<Collection> {
292        match self {
293            CollectionToggle::Set(value) => Some(value),
294            CollectionToggle::Clear => None,
295            CollectionToggle::None => panic!("Tried to convert 'None' value"),
296        }
297    }
298}
299
300#[repr(C)]
301#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
302#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Default)]
303pub enum UsesToggle {
304    #[default]
305    None,
306    Clear,
307    Set(Uses),
308}
309
310impl UsesToggle {
311    pub fn is_some(&self) -> bool {
312        matches!(self, UsesToggle::Clear | UsesToggle::Set(_))
313    }
314
315    pub fn is_none(&self) -> bool {
316        matches!(self, UsesToggle::None)
317    }
318
319    pub fn is_clear(&self) -> bool {
320        matches!(self, UsesToggle::Clear)
321    }
322
323    pub fn is_set(&self) -> bool {
324        matches!(self, UsesToggle::Set(_))
325    }
326
327    pub fn to_option(self) -> Option<Uses> {
328        match self {
329            UsesToggle::Set(value) => Some(value),
330            UsesToggle::Clear => None,
331            UsesToggle::None => panic!("Tried to convert 'None' value"),
332        }
333    }
334}
335
336#[repr(C)]
337#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
338#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Default)]
339pub enum CollectionDetailsToggle {
340    #[default]
341    None,
342    Clear,
343    Set(CollectionDetails),
344}
345
346impl CollectionDetailsToggle {
347    pub fn is_some(&self) -> bool {
348        matches!(
349            self,
350            CollectionDetailsToggle::Clear | CollectionDetailsToggle::Set(_)
351        )
352    }
353
354    pub fn is_none(&self) -> bool {
355        matches!(self, CollectionDetailsToggle::None)
356    }
357
358    pub fn is_clear(&self) -> bool {
359        matches!(self, CollectionDetailsToggle::Clear)
360    }
361
362    pub fn is_set(&self) -> bool {
363        matches!(self, CollectionDetailsToggle::Set(_))
364    }
365
366    pub fn to_option(self) -> Option<CollectionDetails> {
367        match self {
368            CollectionDetailsToggle::Set(value) => Some(value),
369            CollectionDetailsToggle::Clear => None,
370            CollectionDetailsToggle::None => panic!("Tried to convert 'None' value"),
371        }
372    }
373}
374
375#[repr(C)]
376#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
377#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Default)]
378pub enum RuleSetToggle {
379    #[default]
380    None,
381    Clear,
382    Set(Pubkey),
383}
384
385impl RuleSetToggle {
386    pub fn is_some(&self) -> bool {
387        matches!(self, RuleSetToggle::Clear | RuleSetToggle::Set(_))
388    }
389
390    pub fn is_none(&self) -> bool {
391        matches!(self, RuleSetToggle::None)
392    }
393
394    pub fn is_clear(&self) -> bool {
395        matches!(self, RuleSetToggle::Clear)
396    }
397
398    pub fn is_set(&self) -> bool {
399        matches!(self, RuleSetToggle::Set(_))
400    }
401
402    pub fn to_option(self) -> Option<Pubkey> {
403        match self {
404            RuleSetToggle::Set(t) => Some(t),
405            RuleSetToggle::Clear => None,
406            RuleSetToggle::None => panic!("Tried to convert 'None' value"),
407        }
408    }
409}
410
411//-- End Toggle implementation
412
413#[repr(C)]
414#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
415#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
416pub enum MigrateArgs {
417    V1 {
418        migration_type: MigrationType,
419        rule_set: Option<Pubkey>,
420    },
421}
422
423#[repr(C)]
424#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))]
425#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
426/// Args for update call
427pub struct UpdateMetadataAccountArgsV2 {
428    pub data: Option<DataV2>,
429    #[cfg_attr(
430        feature = "serde-feature",
431        serde(with = "As::<Option<DisplayFromStr>>")
432    )]
433    pub update_authority: Option<Pubkey>,
434    pub primary_sale_happened: Option<bool>,
435    pub is_mutable: Option<bool>,
436}
437
438//----------------------+
439// Instruction builders |
440//----------------------+
441
442///# Create Metadata Accounts V3 -- Supports v1.3 Collection Details
443///
444///Create a new Metadata Account
445///
446/// ### Accounts:
447///
448///   0. `[writable]` Metadata account
449///   1. `[]` Mint account
450///   2. `[signer]` Mint authority
451///   3. `[signer]` payer
452///   4. `[signer]` Update authority
453///   5. `[]` System program
454///   6. Optional `[]` Rent sysvar
455///
456/// Creates an CreateMetadataAccounts instruction
457#[allow(clippy::too_many_arguments)]
458pub fn create_metadata_accounts_v3(
459    program_id: Pubkey,
460    metadata_account: Pubkey,
461    mint: Pubkey,
462    mint_authority: Pubkey,
463    payer: Pubkey,
464    update_authority: Pubkey,
465    name: String,
466    symbol: String,
467    uri: String,
468    creators: Option<Vec<Creator>>,
469    seller_fee_basis_points: u16,
470    update_authority_is_signer: bool,
471    is_mutable: bool,
472    collection: Option<Collection>,
473    uses: Option<Uses>,
474    collection_details: Option<CollectionDetails>,
475) -> Instruction {
476    Instruction {
477        program_id,
478        accounts: vec![
479            AccountMeta::new(metadata_account, false),
480            AccountMeta::new_readonly(mint, false),
481            AccountMeta::new_readonly(mint_authority, true),
482            AccountMeta::new(payer, true),
483            AccountMeta::new_readonly(update_authority, update_authority_is_signer),
484            AccountMeta::new_readonly(solana_program::system_program::ID, false),
485        ],
486        data: MetadataInstruction::CreateMetadataAccountV3(CreateMetadataAccountArgsV3 {
487            data: DataV2 {
488                name,
489                symbol,
490                uri,
491                seller_fee_basis_points,
492                creators,
493                collection,
494                uses,
495            },
496            is_mutable,
497            collection_details,
498        })
499        .try_to_vec()
500        .unwrap(),
501    }
502}
503
504/// puff metadata account instruction
505pub fn puff_metadata_account(program_id: Pubkey, metadata_account: Pubkey) -> Instruction {
506    Instruction {
507        program_id,
508        accounts: vec![AccountMeta::new(metadata_account, false)],
509        data: MetadataInstruction::PuffMetadata.try_to_vec().unwrap(),
510    }
511}
512
513/// Remove Creator Verificaton
514#[allow(clippy::too_many_arguments)]
515pub fn remove_creator_verification(
516    program_id: Pubkey,
517    metadata: Pubkey,
518    creator: Pubkey,
519) -> Instruction {
520    Instruction {
521        program_id,
522        accounts: vec![
523            AccountMeta::new(metadata, false),
524            AccountMeta::new_readonly(creator, true),
525        ],
526        data: MetadataInstruction::RemoveCreatorVerification
527            .try_to_vec()
528            .unwrap(),
529    }
530}
531
532pub fn set_token_standard(
533    program_id: Pubkey,
534    metadata_account: Pubkey,
535    update_authority: Pubkey,
536    mint_account: Pubkey,
537    edition_account: Option<Pubkey>,
538) -> Instruction {
539    let mut accounts = vec![
540        AccountMeta::new(metadata_account, false),
541        AccountMeta::new(update_authority, true),
542        AccountMeta::new_readonly(mint_account, false),
543    ];
544    let data = MetadataInstruction::SetTokenStandard.try_to_vec().unwrap();
545
546    if let Some(edition_account) = edition_account {
547        accounts.push(AccountMeta::new_readonly(edition_account, false));
548    }
549
550    Instruction {
551        program_id,
552        accounts,
553        data,
554    }
555}
556
557/// Sign Metadata
558#[allow(clippy::too_many_arguments)]
559pub fn sign_metadata(program_id: Pubkey, metadata: Pubkey, creator: Pubkey) -> Instruction {
560    Instruction {
561        program_id,
562        accounts: vec![
563            AccountMeta::new(metadata, false),
564            AccountMeta::new_readonly(creator, true),
565        ],
566        data: MetadataInstruction::SignMetadata.try_to_vec().unwrap(),
567    }
568}
569
570// update metadata account v2 instruction
571pub fn update_metadata_accounts_v2(
572    program_id: Pubkey,
573    metadata_account: Pubkey,
574    update_authority: Pubkey,
575    new_update_authority: Option<Pubkey>,
576    data: Option<DataV2>,
577    primary_sale_happened: Option<bool>,
578    is_mutable: Option<bool>,
579) -> Instruction {
580    Instruction {
581        program_id,
582        accounts: vec![
583            AccountMeta::new(metadata_account, false),
584            AccountMeta::new_readonly(update_authority, true),
585        ],
586        data: MetadataInstruction::UpdateMetadataAccountV2(UpdateMetadataAccountArgsV2 {
587            data,
588            update_authority: new_update_authority,
589            primary_sale_happened,
590            is_mutable,
591        })
592        .try_to_vec()
593        .unwrap(),
594    }
595}
596
597/// creates a update_primary_sale_happened_via_token instruction
598#[allow(clippy::too_many_arguments)]
599pub fn update_primary_sale_happened_via_token(
600    program_id: Pubkey,
601    metadata: Pubkey,
602    owner: Pubkey,
603    token: Pubkey,
604) -> Instruction {
605    Instruction {
606        program_id,
607        accounts: vec![
608            AccountMeta::new(metadata, false),
609            AccountMeta::new_readonly(owner, true),
610            AccountMeta::new_readonly(token, false),
611        ],
612        data: MetadataInstruction::UpdatePrimarySaleHappenedViaToken
613            .try_to_vec()
614            .unwrap(),
615    }
616}
617
618//-- Instruction Builders trait implementation
619
620/// Builds the instruction to create metadata and associated accounts.
621///
622/// # Accounts:
623///
624///   0. `[writable]` Metadata account
625///   1. `[optional, writable]` Master edition account
626///   2. `[writable]` Mint account
627///   3. `[signer]` Mint authority
628///   4. `[signer]` Payer
629///   5. `[signer]` Update authority
630///   6. `[]` System program
631///   7. `[]` Instructions sysvar account
632///   8. `[]` SPL Token program
633impl InstructionBuilder for super::builders::Create {
634    fn instruction(&self) -> solana_program::instruction::Instruction {
635        let accounts = vec![
636            AccountMeta::new(self.metadata, false),
637            // checks whether we have a master edition
638            if let Some(master_edition) = self.master_edition {
639                AccountMeta::new(master_edition, false)
640            } else {
641                AccountMeta::new_readonly(crate::ID, false)
642            },
643            AccountMeta::new(self.mint, self.initialize_mint),
644            AccountMeta::new_readonly(self.authority, true),
645            AccountMeta::new(self.payer, true),
646            AccountMeta::new_readonly(self.update_authority, self.update_authority_as_signer),
647            AccountMeta::new_readonly(self.system_program, false),
648            AccountMeta::new_readonly(self.sysvar_instructions, false),
649            AccountMeta::new_readonly(self.spl_token_program, false),
650        ];
651
652        Instruction {
653            program_id: crate::ID,
654            accounts,
655            data: MetadataInstruction::Create(self.args.clone())
656                .try_to_vec()
657                .unwrap(),
658        }
659    }
660}
661
662/// Migrates an asset to a ProgrammableAsset type.
663///
664/// # Accounts:
665///
666///
667///   0. `[writable]` Metadata account
668///   1. `[writable]` Edition account
669///   2. `[writable]` Token account
670///   3. `[]` Token account owner
671///   4. `[]` Mint account
672///   5. `[writable, signer]` Payer
673///   6. `[signer]` Update authority
674///   7. `[]` Collection metadata account
675///   8. `[]` Delegate record account
676///   9. `[writable]` Token record account
677///   10. `[]` System program
678///   11. `[]` Instruction sysvar account
679///   12. `[]` SPL Token Program
680///   13. `[optional]` Token Authorization Rules Program
681///   14. `[optional]` Token Authorization Rules account
682impl InstructionBuilder for super::builders::Migrate {
683    fn instruction(&self) -> solana_program::instruction::Instruction {
684        let mut accounts = vec![
685            AccountMeta::new(self.metadata, false),
686            AccountMeta::new(self.edition, false),
687            AccountMeta::new(self.token, false),
688            AccountMeta::new_readonly(self.token_owner, false),
689            AccountMeta::new_readonly(self.mint, false),
690            AccountMeta::new(self.payer, true),
691            AccountMeta::new_readonly(self.authority, true),
692            AccountMeta::new_readonly(self.collection_metadata, false),
693            AccountMeta::new_readonly(self.delegate_record, false),
694            AccountMeta::new(self.token_record, false),
695            AccountMeta::new_readonly(self.system_program, false),
696            AccountMeta::new_readonly(self.sysvar_instructions, false),
697            AccountMeta::new_readonly(self.spl_token_program, false),
698        ];
699
700        // Optional authorization rules accounts
701        if let Some(rules) = &self.authorization_rules {
702            accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false));
703            accounts.push(AccountMeta::new_readonly(*rules, false));
704        } else {
705            accounts.push(AccountMeta::new_readonly(crate::ID, false));
706            accounts.push(AccountMeta::new_readonly(crate::ID, false));
707        }
708
709        Instruction {
710            program_id: crate::ID,
711            accounts,
712            data: MetadataInstruction::Migrate(self.args.clone())
713                .try_to_vec()
714                .unwrap(),
715        }
716    }
717}
718
719/// Builds the instruction to mint a token.
720///
721/// # Accounts:
722///
723///   0. `[writable]` Token account key
724///   1. `[optional]` Owner of the token account
725///   2. `[]` Metadata account key (pda of ['metadata', program id, mint id])")]
726///   3. `[optional]` Master Edition account
727///   4. `[optional]` Token record account
728///   5. `[writable]` Mint of token asset
729///   6. `[signer]` Authority (mint authority or metadata's update authority for NonFungible asests)
730///   7. `[optional]` Metadata delegate record
731///   8. `[signer, writable]` Payer
732///   9. `[]` System program
733///   10. `[]` Instructions sysvar account
734///   11. `[]` SPL Token program
735///   12. `[]` SPL Associated Token Account program
736///   13. `[optional]` Token Authorization Rules program
737///   14. `[optional]` Token Authorization Rules account
738impl InstructionBuilder for super::builders::Mint {
739    fn instruction(&self) -> solana_program::instruction::Instruction {
740        let mut accounts = vec![
741            AccountMeta::new(self.token, false),
742            AccountMeta::new_readonly(self.token_owner.unwrap_or(crate::ID), false),
743            AccountMeta::new_readonly(self.metadata, false),
744            if let Some(master_edition) = self.master_edition {
745                AccountMeta::new(master_edition, false)
746            } else {
747                AccountMeta::new_readonly(crate::ID, false)
748            },
749            if let Some(token_record) = self.token_record {
750                AccountMeta::new(token_record, false)
751            } else {
752                AccountMeta::new_readonly(crate::ID, false)
753            },
754            AccountMeta::new(self.mint, false),
755            AccountMeta::new_readonly(self.authority, true),
756            AccountMeta::new_readonly(self.delegate_record.unwrap_or(crate::ID), false),
757            AccountMeta::new(self.payer, true),
758            AccountMeta::new_readonly(self.system_program, false),
759            AccountMeta::new_readonly(self.sysvar_instructions, false),
760            AccountMeta::new_readonly(self.spl_token_program, false),
761            AccountMeta::new_readonly(self.spl_ata_program, false),
762        ];
763        // Optional authorization rules accounts
764        if let Some(rules) = &self.authorization_rules {
765            accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false));
766            accounts.push(AccountMeta::new_readonly(*rules, false));
767        } else {
768            accounts.push(AccountMeta::new_readonly(crate::ID, false));
769            accounts.push(AccountMeta::new_readonly(crate::ID, false));
770        }
771
772        Instruction {
773            program_id: crate::ID,
774            accounts,
775            data: MetadataInstruction::Mint(self.args.clone())
776                .try_to_vec()
777                .unwrap(),
778        }
779    }
780}
781
782/// Transfer tokens from a token account.
783///
784/// # Accounts:
785///
786///   0. `[writable]` Token account
787///   1. `[]` Token account owner
788///   2. `[writable]` Destination token account
789///   3. `[]` Destination token account owner
790///   4. `[]` Mint of token asset
791///   5. `[writable]` Metadata account
792///   6. `[optional]` Edition of token asset
793///   7. `[optional, writable]` Owner token record account
794///   8. `[optional, writable]` Destination token record account
795///   9. `[signer]` Transfer authority (token owner or delegate)
796///   10. `[signer, writable]` Payer
797///   11. `[]` System Program
798///   12. `[]` Instructions sysvar account
799///   13. `[]` SPL Token Program
800///   14. `[]` SPL Associated Token Account program
801///   15. `[optional]` Token Authorization Rules Program
802///   16. `[optional]` Token Authorization Rules account
803impl InstructionBuilder for super::builders::Transfer {
804    fn instruction(&self) -> solana_program::instruction::Instruction {
805        let mut accounts = vec![
806            AccountMeta::new(self.token, false),
807            AccountMeta::new_readonly(self.token_owner, false),
808            AccountMeta::new(self.destination, false),
809            AccountMeta::new_readonly(self.destination_owner, false),
810            AccountMeta::new_readonly(self.mint, false),
811            AccountMeta::new(self.metadata, false),
812            AccountMeta::new_readonly(self.edition.unwrap_or(crate::ID), false),
813            if let Some(owner_token_record) = self.owner_token_record {
814                AccountMeta::new(owner_token_record, false)
815            } else {
816                AccountMeta::new_readonly(crate::ID, false)
817            },
818            if let Some(destination_token_record) = self.destination_token_record {
819                AccountMeta::new(destination_token_record, false)
820            } else {
821                AccountMeta::new_readonly(crate::ID, false)
822            },
823            AccountMeta::new_readonly(self.authority, true),
824            AccountMeta::new(self.payer, true),
825            AccountMeta::new_readonly(self.system_program, false),
826            AccountMeta::new_readonly(self.sysvar_instructions, false),
827            AccountMeta::new_readonly(self.spl_token_program, false),
828            AccountMeta::new_readonly(self.spl_ata_program, false),
829        ];
830        // Optional authorization rules accounts
831        if let Some(rules) = &self.authorization_rules {
832            accounts.push(AccountMeta::new_readonly(
833                self.authorization_rules_program.unwrap_or(crate::ID),
834                false,
835            ));
836            accounts.push(AccountMeta::new_readonly(*rules, false));
837        } else {
838            accounts.push(AccountMeta::new_readonly(crate::ID, false));
839            accounts.push(AccountMeta::new_readonly(crate::ID, false));
840        }
841
842        Instruction {
843            program_id: crate::ID,
844            accounts,
845            data: MetadataInstruction::Transfer(self.args.clone())
846                .try_to_vec()
847                .unwrap(),
848        }
849    }
850}
851
852/// Updates the metadata of an asset.
853///
854/// # Accounts:
855///
856///   0. `[signer]` Update authority or delegate
857///   1. `[optional]` Delegate record PDA
858///   2. `[optional]` Token account
859///   3. `[]` Mint account
860///   4. `[writable]` Metadata account
861///   5. `[optional]` Edition account
862///   6. `[signer]` Payer
863///   7. `[]` System program
864///   8. `[]` System program
865///   9. `[optional]` Token Authorization Rules Program
866///   10. `[optional]` Token Authorization Rules account
867impl InstructionBuilder for super::builders::Update {
868    fn instruction(&self) -> solana_program::instruction::Instruction {
869        let mut accounts = vec![
870            AccountMeta::new_readonly(self.authority, true),
871            AccountMeta::new_readonly(self.delegate_record.unwrap_or(crate::ID), false),
872            AccountMeta::new_readonly(self.token.unwrap_or(crate::ID), false),
873            AccountMeta::new_readonly(self.mint, false),
874            AccountMeta::new(self.metadata, false),
875            AccountMeta::new_readonly(self.edition.unwrap_or(crate::ID), false),
876            AccountMeta::new(self.payer, true),
877            AccountMeta::new_readonly(self.system_program, false),
878            AccountMeta::new_readonly(self.sysvar_instructions, false),
879        ];
880
881        // Optional authorization rules accounts
882        if let Some(rules) = &self.authorization_rules {
883            accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false));
884            accounts.push(AccountMeta::new_readonly(*rules, false));
885        } else {
886            accounts.push(AccountMeta::new_readonly(crate::ID, false));
887            accounts.push(AccountMeta::new_readonly(crate::ID, false));
888        }
889
890        Instruction {
891            program_id: crate::ID,
892            accounts,
893            data: MetadataInstruction::Update(self.args.clone())
894                .try_to_vec()
895                .unwrap(),
896        }
897    }
898}