nifty_asset_interface/
processor.rs

1use solana_program::{
2    account_info::AccountInfo,
3    entrypoint::ProgramResult,
4    instruction::{AccountMeta, Instruction},
5    program::invoke_signed,
6    program_error::ProgramError,
7    pubkey::Pubkey,
8};
9
10use crate::Interface;
11
12/// Fetches the data for a proxy asset.
13///
14/// This macro is used to fetch the data from the proxy extension of an asset.
15///
16/// # Arguments
17///
18/// 1. `data` - expression representing the account data.
19/// 2. `signer` - identifier to store the signer seeds.
20/// 3. `authority` - identifier to store the authority pubkey.
21/// 4. `program` - (optional) identifier to store the program pubkey.
22/// 5. `remaining` - (optional) expression representing the remaining accounts array.
23#[macro_export]
24macro_rules! fetch_proxy_data {
25    ( $data:expr, $signer:ident, $authority:ident ) => {
26        // proxy
27        let __proxy = $crate::state::Asset::get::<$crate::extensions::Proxy>($data)
28            .ok_or(solana_program::program_error::ProgramError::InvalidAccountData)?;
29
30        let __seeds = *__proxy.seeds;
31        let $signer = [__seeds.as_ref(), &[*__proxy.bump]];
32
33        let $authority = if let Some(authority) = __proxy.authority.value() {
34            Some(*authority)
35        } else {
36            None
37        };
38    };
39
40    ( $data:expr, $signer:ident, $authority:ident, $program:ident, $remaining:expr ) => {
41        fetch_proxy_data!($data, $signer, $authority);
42
43        let $program = $remaining
44            .iter()
45            .find(|account| {
46                account.key
47                    == &solana_program::pubkey!("AssetGtQBTSgm5s91d1RAQod5JmaZiJDxqsgtqrZud73")
48            })
49            .ok_or(solana_program::program_error::ProgramError::NotEnoughAccountKeys)?;
50    };
51}
52
53/// Logs the name of the instruction.
54///
55/// The macro use the first byte of the instruction data to determine
56/// the name of the instruction.
57macro_rules! log_instruction_name {
58    ( $instruction_data:expr ) => {{
59        if $instruction_data.is_empty() {
60            return Err(solana_program::program_error::ProgramError::InvalidInstructionData);
61        }
62
63        let name = match $instruction_data[0] {
64            0 => "Close",
65            1 => "Burn",
66            2 => "Create",
67            3 => "Approve",
68            4 => "Allocate",
69            5 => "Lock",
70            6 => "Revoke",
71            7 => "Transfer",
72            8 => "Unlock",
73            9 => "Unverify",
74            10 => "Update",
75            11 => "Verify",
76            12 => "Write",
77            13 => "Group",
78            14 => "Ungroup",
79            15 => "Handover",
80            16 => "Remove",
81            _ => return Err(solana_program::program_error::ProgramError::InvalidInstructionData),
82        };
83
84        solana_program::msg!("Interface: {}", name);
85    }};
86}
87
88impl Interface {
89    /// Processes the instruction for the asset program.
90    pub fn process_instruction<'a>(
91        _program_id: &'a Pubkey,
92        accounts: &'a [AccountInfo<'a>],
93        instruction_data: &[u8],
94    ) -> ProgramResult {
95        log_instruction_name!(instruction_data);
96
97        let asset = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
98        let data = (*asset.data).borrow();
99        fetch_proxy_data!(&data, signer, _authority);
100        // drop the data before invoking the CPI
101        drop(data);
102
103        let mut account_metas = Vec::with_capacity(accounts.len());
104        accounts.iter().for_each(|account| {
105            account_metas.push(AccountMeta {
106                pubkey: *account.key,
107                is_signer: account.is_signer,
108                is_writable: account.is_writable,
109            });
110        });
111        // the asset account must be a signer
112        account_metas.first_mut().unwrap().is_signer = true;
113
114        invoke_signed(
115            &Instruction {
116                accounts: account_metas,
117                data: instruction_data.to_vec(),
118                program_id: solana_program::pubkey!("AssetGtQBTSgm5s91d1RAQod5JmaZiJDxqsgtqrZud73"),
119            },
120            accounts,
121            &[&signer],
122        )
123    }
124}