Skip to main content

pinocchio_token/instructions/
revoke.rs

1use {
2    crate::{
3        instructions::{
4            account_borrow_failed_error, invalid_argument_error, CpiWriter, MAX_MULTISIG_SIGNERS,
5        },
6        UNINIT_BYTE, UNINIT_CPI_ACCOUNT, UNINIT_INSTRUCTION_ACCOUNT,
7    },
8    core::{mem::MaybeUninit, slice::from_raw_parts},
9    solana_account_view::AccountView,
10    solana_instruction_view::{
11        cpi::{invoke_signed_unchecked, CpiAccount, Signer},
12        InstructionAccount, InstructionView,
13    },
14    solana_program_error::{ProgramError, ProgramResult},
15};
16
17/// Revokes the delegate's authority.
18///
19/// Accounts expected by this instruction:
20///
21///   * Single owner
22///   0. `[writable]` The source account.
23///   1. `[signer]` The source account's owner/delegate.
24///
25///   * Multisignature owner/delegate
26///   0. `[writable]` The source account.
27///   1. `[]` The source account's multisignature owner/delegate.
28///   2. `..+M` `[signer]` M signer accounts.
29pub struct Revoke<'account, 'multisig, MultisigSigner: AsRef<AccountView>> {
30    /// The source account.
31    pub source: &'account AccountView,
32
33    /// The source account's owner/delegate.
34    pub authority: &'account AccountView,
35
36    /// Multisignature signers.
37    pub multisig_signers: &'multisig [MultisigSigner],
38}
39
40impl<'account> Revoke<'account, '_, &'account AccountView> {
41    /// The instruction discriminator.
42    pub const DISCRIMINATOR: u8 = 5;
43
44    /// Maximum number of accounts expected by this instruction.
45    ///
46    /// The required number of accounts will depend whether the
47    /// source account has a single owner or a multisignature
48    /// owner.
49    pub const MAX_ACCOUNTS_LEN: usize = 2 + MAX_MULTISIG_SIGNERS;
50
51    /// Instruction data length:
52    ///   - discriminator (1 byte)
53    pub const DATA_LEN: usize = 1;
54
55    /// Creates a new `Revoke` instruction with a single owner authority.
56    #[inline(always)]
57    pub fn new(source: &'account AccountView, authority: &'account AccountView) -> Self {
58        Self::with_multisig_signers(source, authority, &[])
59    }
60}
61
62impl<'account, 'multisig, MultisigSigner: AsRef<AccountView>>
63    Revoke<'account, 'multisig, MultisigSigner>
64{
65    /// Creates a new `Revoke` instruction with a
66    /// multisignature owner authority and signer accounts.
67    #[inline(always)]
68    pub fn with_multisig_signers(
69        source: &'account AccountView,
70        authority: &'account AccountView,
71        multisig_signers: &'multisig [MultisigSigner],
72    ) -> Self {
73        Self {
74            source,
75            authority,
76            multisig_signers,
77        }
78    }
79
80    #[inline(always)]
81    pub fn invoke(&self) -> ProgramResult {
82        self.invoke_signed(&[])
83    }
84
85    #[inline(always)]
86    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
87        if self.multisig_signers.len() > MAX_MULTISIG_SIGNERS {
88            Err(ProgramError::InvalidArgument)?;
89        }
90
91        let mut instruction_accounts = [UNINIT_INSTRUCTION_ACCOUNT; Revoke::MAX_ACCOUNTS_LEN];
92        let written_instruction_accounts =
93            self.write_instruction_accounts(&mut instruction_accounts)?;
94
95        let mut accounts = [UNINIT_CPI_ACCOUNT; Revoke::MAX_ACCOUNTS_LEN];
96        let written_accounts = self.write_accounts(&mut accounts)?;
97
98        let mut instruction_data = [UNINIT_BYTE; Revoke::DATA_LEN];
99        let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
100
101        unsafe {
102            invoke_signed_unchecked(
103                &InstructionView {
104                    program_id: &crate::ID,
105                    accounts: from_raw_parts(
106                        instruction_accounts.as_ptr() as _,
107                        written_instruction_accounts,
108                    ),
109                    data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
110                },
111                from_raw_parts(accounts.as_ptr() as _, written_accounts),
112                signers,
113            );
114        }
115
116        Ok(())
117    }
118}
119
120impl<MultisigSigner: AsRef<AccountView>> CpiWriter for Revoke<'_, '_, MultisigSigner> {
121    #[inline(always)]
122    fn write_accounts<'cpi>(
123        &self,
124        accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
125    ) -> Result<usize, ProgramError>
126    where
127        Self: 'cpi,
128    {
129        write_accounts(self.source, self.authority, self.multisig_signers, accounts)
130    }
131
132    #[inline(always)]
133    fn write_instruction_accounts<'cpi>(
134        &self,
135        accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
136    ) -> Result<usize, ProgramError>
137    where
138        Self: 'cpi,
139    {
140        write_instruction_accounts(self.source, self.authority, self.multisig_signers, accounts)
141    }
142
143    #[inline(always)]
144    fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
145        write_instruction_data(data)
146    }
147}
148
149impl<MultisigSigner: AsRef<AccountView>> super::IntoBatch for Revoke<'_, '_, MultisigSigner> {
150    #[inline(always)]
151    fn into_batch<'account, 'state>(
152        self,
153        batch: &mut super::Batch<'account, 'state>,
154    ) -> ProgramResult
155    where
156        Self: 'account + 'state,
157    {
158        batch.push(
159            |accounts| write_accounts(self.source, self.authority, self.multisig_signers, accounts),
160            |accounts| {
161                write_instruction_accounts(
162                    self.source,
163                    self.authority,
164                    self.multisig_signers,
165                    accounts,
166                )
167            },
168            write_instruction_data,
169        )
170    }
171}
172
173#[inline(always)]
174fn write_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
175    source: &'account AccountView,
176    authority: &'account AccountView,
177    multisig_signers: &'multisig [MultisigSigner],
178    accounts: &mut [MaybeUninit<CpiAccount<'out>>],
179) -> Result<usize, ProgramError>
180where
181    'account: 'out,
182    'multisig: 'out,
183{
184    let expected_accounts = 2 + multisig_signers.len();
185
186    if expected_accounts > accounts.len() {
187        return Err(invalid_argument_error());
188    }
189
190    if source.is_borrowed() {
191        return Err(account_borrow_failed_error());
192    }
193
194    CpiAccount::init_from_account_view(source, &mut accounts[0]);
195
196    CpiAccount::init_from_account_view(authority, &mut accounts[1]);
197
198    for (account, signer) in accounts[2..expected_accounts]
199        .iter_mut()
200        .zip(multisig_signers.iter())
201    {
202        CpiAccount::init_from_account_view(signer.as_ref(), account);
203    }
204
205    Ok(expected_accounts)
206}
207
208#[inline(always)]
209fn write_instruction_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
210    source: &'account AccountView,
211    authority: &'account AccountView,
212    multisig_signers: &'multisig [MultisigSigner],
213    accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
214) -> Result<usize, ProgramError>
215where
216    'account: 'out,
217    'multisig: 'out,
218{
219    let expected_accounts = 2 + multisig_signers.len();
220
221    if expected_accounts > accounts.len() {
222        return Err(invalid_argument_error());
223    }
224
225    accounts[0].write(InstructionAccount::writable(source.address()));
226
227    accounts[1].write(InstructionAccount::new(
228        authority.address(),
229        false,
230        multisig_signers.is_empty(),
231    ));
232
233    for (account, signer) in accounts[2..expected_accounts]
234        .iter_mut()
235        .zip(multisig_signers.iter())
236    {
237        account.write(InstructionAccount::readonly_signer(
238            signer.as_ref().address(),
239        ));
240    }
241
242    Ok(expected_accounts)
243}
244
245#[inline(always)]
246fn write_instruction_data(data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
247    if data.len() < Revoke::DATA_LEN {
248        return Err(invalid_argument_error());
249    }
250
251    data[0].write(Revoke::DISCRIMINATOR);
252
253    Ok(Revoke::DATA_LEN)
254}