rialo_sol_derive_accounts/lib.rs
1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4#[allow(unused_extern_crates)]
5extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use quote::ToTokens;
9use syn::parse_macro_input;
10
11/// Implements an [`Accounts`](./trait.Accounts.html) deserializer on the given
12/// struct. Can provide further functionality through the use of attributes.
13///
14/// # Table of Contents
15/// - [Instruction Attribute](#instruction-attribute)
16/// - [Constraints](#constraints)
17///
18/// # Instruction Attribute
19///
20/// You can access the instruction's arguments with the
21/// `#[instruction(..)]` attribute. You have to list them
22/// in the same order as in the instruction but you can
23/// omit all arguments after the last one you need.
24///
25/// # Example
26///
27/// ```ignore
28/// ...
29/// pub fn initialize(ctx: Context<Create>, bump: u8, authority: Pubkey, data: u64) -> rialo_sol_lang::Result<()> {
30/// ...
31/// Ok(())
32/// }
33/// ...
34/// #[derive(Accounts)]
35/// #[instruction(bump: u8)]
36/// pub struct Initialize<'info> {
37/// ...
38/// }
39/// ```
40///
41/// # Constraints
42///
43/// There are different types of constraints that can be applied with the `#[account(..)]` attribute.
44///
45/// Attributes may reference other data structures. When `<expr>` is used in the tables below, an arbitrary expression
46/// may be passed in as long as it evaluates to a value of the expected type, e.g. `owner = token_program.key()`. If `target_account`
47/// used, the `target_account` must exist in the struct and the `.key()` is implicit, e.g. `payer = authority`.
48///
49/// - [Normal Constraints](#normal-constraints)
50/// - [SPL Constraints](#spl-constraints)
51///
52/// # Normal Constraints
53/// <table>
54/// <thead>
55/// <tr>
56/// <th>Attribute</th>
57/// <th>Description</th>
58/// </tr>
59/// </thead>
60/// <tbody>
61/// <tr>
62/// <td>
63/// <code>#[account(signer)]</code> <br><br><code>#[account(signer @ <custom_error>)]</code>
64/// </td>
65/// <td>
66/// Checks the given account signed the transaction.<br>
67/// Custom errors are supported via <code>@</code>.<br>
68/// Consider using the <code>Signer</code> type if you would only have this constraint on the account.<br><br>
69/// Example:
70/// <pre><code>
71/// #[account(signer)]
72/// pub authority: AccountInfo<'info>,
73/// #[account(signer @ MyError::MyErrorCode)]
74/// pub payer: AccountInfo<'info>
75/// </code></pre>
76/// </td>
77/// </tr>
78/// <tr>
79/// <td>
80/// <code>#[account(mut)]</code> <br><br><code>#[account(mut @ <custom_error>)]</code>
81/// </td>
82/// <td>
83/// Checks the given account is mutable.<br>
84/// Makes anchor persist any state changes.<br>
85/// Custom errors are supported via <code>@</code>.<br><br>
86/// Example:
87/// <pre><code>
88/// #[account(mut)]
89/// pub data_account: Account<'info, MyData>,
90/// #[account(mut @ MyError::MyErrorCode)]
91/// pub data_account_two: Account<'info, MyData>
92/// </code></pre>
93/// </td>
94/// </tr>
95/// <tr>
96/// <td>
97/// <code>#[account(init, payer = <target_account>, space = <num_bytes>)]</code>
98/// </td>
99/// <td>
100/// Creates the account via a CPI to the system program and
101/// initializes it (sets its account discriminator). The annotated account is required to sign for this instruction
102/// unless `seeds` is provided. <br>
103/// Marks the account as mutable and is mutually exclusive with <code>mut</code>.<br>
104/// Makes the account rent exempt unless skipped with <code>rent_exempt = skip</code>.<br><br>
105/// Use <code>#[account(zero)]</code> for accounts larger than 10 Kibibyte.<br><br>
106/// <code>init</code> has to be used with additional constraints:
107/// <ul>
108/// <li>
109/// Requires the <code>payer</code> constraint to also be on the account.
110/// The <code>payer</code> account pays for the
111/// account creation.
112/// </li>
113/// <li>
114/// Requires the system program to exist on the struct
115/// and be called <code>system_program</code>.
116/// </li>
117/// <li>
118/// Requires that the <code>space</code> constraint is specified.
119/// When using the <code>space</code> constraint, one must remember to add 8 to it
120/// which is the size of the account discriminator. This only has to be done
121/// for accounts owned by anchor programs.<br>
122/// The given space number is the size of the account in bytes, so accounts that hold
123/// a variable number of items such as a <code>Vec</code> should allocate sufficient space for all items that may
124/// be added to the data structure because account size is fixed.
125/// Check out the <a href = "https://www.anchor-lang.com/docs/space" target = "_blank" rel = "noopener noreferrer">space reference</a>
126/// and the <a href = "https://borsh.io/" target = "_blank" rel = "noopener noreferrer">borsh library</a>
127/// (which anchor uses under the hood for serialization) specification to learn how much
128/// space different data structures require.
129/// </li>
130/// <br>
131/// Example:
132/// <pre>
133/// #[account]
134/// pub struct MyData {
135/// pub data: u64
136/// }
137/// #[derive(Accounts)]
138/// pub struct Initialize<'info> {
139/// #[account(init, payer = payer, space = 8 + 8)]
140/// pub data_account_two: Account<'info, MyData>,
141/// #[account(mut)]
142/// pub payer: Signer<'info>,
143/// pub system_program: Program<'info, System>,
144/// }
145/// </pre>
146/// </ul>
147/// <code>init</code> can be combined with other constraints (at the same time):
148/// <ul>
149/// <li>
150/// By default <code>init</code> sets the owner field of the created account to the
151/// currently executing program. Add the <code>owner</code> constraint to specify a
152/// different program owner.
153/// </li>
154/// <li>
155/// Use the <code>seeds</code> constraint together with <code>bump</code>to create PDAs.<br>
156/// <code>init</code> uses <code>find_program_address</code> to calculate the pda so the
157/// bump value can be left empty.<br>
158/// However, if you want to use the bump in your instruction,
159/// you can pass it in as instruction data and set the bump value like shown in the example,
160/// using the <code>instruction_data</code> attribute.
161/// Anchor will then check that the bump returned by <code>find_program_address</code> equals
162/// the bump in the instruction data.<br>
163/// <code>seeds::program</code> cannot be used together with init because the creation of an
164/// account requires its signature which for PDAs only the currently executing program can provide.
165/// </li>
166/// </ul>
167/// Example:
168/// <pre>
169/// #[derive(Accounts)]
170/// #[instruction(bump: u8)]
171/// pub struct Initialize<'info> {
172/// #[account(
173/// init, payer = payer, space = 8 + 8
174/// seeds = [b"example_seed"], bump = bump
175/// )]
176/// pub pda_data_account: Account<'info, MyData>,
177/// #[account(
178/// init, payer = payer,
179/// space = 8 + 8, owner = other_program.key()
180/// )]
181/// pub account_for_other_program: AccountInfo<'info>,
182/// #[account(
183/// init, payer = payer, space = 8 + 8,
184/// owner = other_program.key(),
185/// seeds = [b"other_seed"], bump
186/// )]
187/// pub pda_for_other_program: AccountInfo<'info>,
188/// #[account(mut)]
189/// pub payer: Signer<'info>,
190/// pub system_program: Program<'info, System>,
191/// pub other_program: Program<'info, OtherProgram>
192/// }
193/// </pre>
194/// </td>
195/// </tr>
196/// <tr>
197/// <td>
198/// <code>#[account(init_if_needed, payer = <target_account>)]</code><br><br>
199/// <code>#[account(init_if_needed, payer = <target_account>, space = <num_bytes>)]</code>
200/// </td>
201/// <td>
202/// Exact same functionality as the <code>init</code> constraint but only runs if the account does not exist yet.<br>
203/// If the account does exist, it still checks whether the given init constraints are correct,
204/// e.g. that the account has the expected amount of space and, if it's a PDA, the correct seeds etc.<br><br>
205/// This feature should be used with care and is therefore behind a feature flag.
206/// You can enable it by importing <code>anchor-lang</code> with the <code>init-if-needed</code> cargo feature.<br>
207/// When using <code>init_if_needed</code>, you need to make sure you properly protect yourself
208/// against re-initialization attacks. You need to include checks in your code that check
209/// that the initialized account cannot be reset to its initial settings after the first time it was
210/// initialized (unless that it what you want).<br>
211/// Because of the possibility of re-initialization attacks and the general guideline that instructions
212/// should avoid having multiple execution flows (which is important so they remain easy to understand),
213/// consider breaking up your instruction into two instructions - one for initializing and one for using
214/// the account - unless you have a good reason not to do so.
215/// <br><br>
216/// Example:
217/// <pre>
218/// #[account]
219/// #[derive(Default)]
220/// pub struct MyData {
221/// pub data: u64
222/// }
223/// #[account]
224/// pub struct OtherData {
225/// pub data: u64
226/// }
227/// #[derive(Accounts)]
228/// pub struct Initialize<'info> {
229/// #[account(init_if_needed, payer = payer)]
230/// pub data_account: Account<'info, MyData>,
231/// #[account(init_if_needed, payer = payer, space = 8 + 8)]
232/// pub data_account_two: Account<'info, OtherData>,
233/// #[account(mut)]
234/// pub payer: Signer<'info>,
235/// pub system_program: Program<'info, System>
236/// }
237/// </pre>
238/// </td>
239/// </tr>
240/// <tr>
241/// <td>
242/// <code>#[account(seeds = <seeds>, bump)]</code><br><br>
243/// <code>#[account(seeds = <seeds>, bump, seeds::program = <expr>)]</code><br><br>
244/// <code>#[account(seeds = <seeds>, bump = <expr>)]</code><br><br>
245/// <code>#[account(seeds = <seeds>, bump = <expr>, seeds::program = <expr>)]</code><br><br>
246/// </td>
247/// <td>
248/// Checks that given account is a PDA derived from the currently executing program,
249/// the seeds, and if provided, the bump. If not provided, anchor uses the canonical
250/// bump. <br>
251/// Add <code>seeds::program = <expr></code> to derive the PDA from a different
252/// program than the currently executing one.<br>
253/// This constraint behaves slightly differently when used with <code>init</code>.
254/// See its description.
255/// <br><br>
256/// Example:
257/// <pre><code>
258/// #[derive(Accounts)]
259/// #[instruction(first_bump: u8, second_bump: u8)]
260/// pub struct Example {
261/// #[account(seeds = [b"example_seed"], bump)]
262/// pub canonical_pda: AccountInfo<'info>,
263/// #[account(
264/// seeds = [b"example_seed"],
265/// bump,
266/// seeds::program = other_program.key()
267/// )]
268/// pub canonical_pda_two: AccountInfo<'info>,
269/// #[account(seeds = [b"other_seed"], bump = first_bump)]
270/// pub arbitrary_pda: AccountInfo<'info>
271/// #[account(
272/// seeds = [b"other_seed"],
273/// bump = second_bump,
274/// seeds::program = other_program.key()
275/// )]
276/// pub arbitrary_pda_two: AccountInfo<'info>,
277/// pub other_program: Program<'info, OtherProgram>
278/// }
279/// </code></pre>
280/// </td>
281/// </tr>
282/// <tr>
283/// <td>
284/// <code>#[account(has_one = <target_account>)]</code><br><br>
285/// <code>#[account(has_one = <target_account> @ <custom_error>)]</code>
286/// </td>
287/// <td>
288/// Checks the <code>target_account</code> field on the account matches the
289/// key of the <code>target_account</code> field in the Accounts struct.<br>
290/// Custom errors are supported via <code>@</code>.<br><br>
291/// Example:
292/// <pre><code>
293/// #[account(mut, has_one = authority)]
294/// pub data: Account<'info, MyData>,
295/// pub authority: Signer<'info>
296/// </code></pre>
297/// In this example <code>has_one</code> checks that <code>data.authority = authority.key()</code>
298/// </td>
299/// </tr>
300/// <tr>
301/// <td>
302/// <code>#[account(address = <expr>)]</code><br><br>
303/// <code>#[account(address = <expr> @ <custom_error>)]</code>
304/// </td>
305/// <td>
306/// Checks the account key matches the pubkey.<br>
307/// Custom errors are supported via <code>@</code>.<br><br>
308/// Example:
309/// <pre><code>
310/// #[account(address = crate::ID)]
311/// pub data: Account<'info, MyData>,
312/// #[account(address = crate::ID @ MyError::MyErrorCode)]
313/// pub data_two: Account<'info, MyData>
314/// </code></pre>
315/// </td>
316/// </tr>
317/// <tr>
318/// <td>
319/// <code>#[account(owner = <expr>)]</code><br><br>
320/// <code>#[account(owner = <expr> @ <custom_error>)]</code>
321/// </td>
322/// <td>
323/// Checks the account owner matches <code>expr</code>.<br>
324/// Custom errors are supported via <code>@</code>.<br><br>
325/// Example:
326/// <pre><code>
327/// #[account(owner = Token::ID @ MyError::MyErrorCode)]
328/// pub data: Account<'info, MyData>,
329/// #[account(owner = token_program.key())]
330/// pub data_two: Account<'info, MyData>,
331/// pub token_program: Program<'info, Token>
332/// </code></pre>
333/// </td>
334/// </tr>
335/// <tr>
336/// <td>
337/// <code>#[account(executable)]</code>
338/// </td>
339/// <td>
340/// Checks the account is executable (i.e. the account is a program).<br>
341/// You may want to use the <code>Program</code> type instead.<br><br>
342/// Example:
343/// <pre><code>
344/// #[account(executable)]
345/// pub my_program: AccountInfo<'info>
346/// </code></pre>
347/// </td>
348/// </tr>
349/// <tr>
350/// <td>
351/// <code>#[account(rent_exempt = skip)]</code><br><br>
352/// <code>#[account(rent_exempt = enforce)]</code>
353/// </td>
354/// <td>
355/// Enforces rent exemption with <code>= enforce</code>.<br>
356/// Skips rent exemption check that would normally be done
357/// through other constraints with <code>= skip</code>,
358/// e.g. when used with the <code>zero</code> constraint<br><br>
359/// Example:
360/// <pre><code>
361/// #[account(zero, rent_exempt = skip)]
362/// pub skipped_account: Account<'info, MyData>,
363/// #[account(rent_exempt = enforce)]
364/// pub enforced_account: AccountInfo<'info>
365/// </code></pre>
366/// </td>
367/// </tr>
368/// <tr>
369/// <td>
370/// <code>#[account(zero)]</code>
371/// </td>
372/// <td>
373/// Checks the account discriminator is zero.<br>
374/// Enforces rent exemption unless skipped with <code>rent_exempt = skip</code>.<br><br>
375/// Use this constraint if you want to create an account in a previous instruction
376/// and then initialize it in your instruction instead of using <code>init</code>.
377/// This is necessary for accounts that are larger than 10 Kibibyte because those
378/// accounts cannot be created via a CPI (which is what <code>init</code> would do).<br><br>
379/// Anchor adds internal data to the account when using <code>zero</code> just like it
380/// does with <code>init</code> which is why <code>zero</code> implies <code>mut</code>.
381/// <br><br>
382/// Example:
383/// <pre><code>
384/// #[account(zero)]
385/// pub my_account: Account<'info, MyData>
386/// </code></pre>
387/// </td>
388/// </tr>
389/// <tr>
390/// <td>
391/// <code>#[account(close = <target_account>)]</code>
392/// </td>
393/// <td>
394/// Closes the account by:<br>
395/// - Sending the kelvins to the specified account<br>
396/// - Assigning the owner to the System Program<br>
397/// - Resetting the data of the account<br><br>
398/// Requires <code>mut</code> to exist on the account.
399/// <br><br>
400/// Example:
401/// <pre><code>
402/// #[account(mut, close = receiver)]
403/// pub data_account: Account<'info, MyData>,
404/// #[account(mut)]
405/// pub receiver: SystemAccount<'info>
406/// </code></pre>
407/// </td>
408/// </tr>
409/// <tr>
410/// <td>
411/// <code>#[account(constraint = <expr>)]</code><br><br><code>#[account(constraint = <expr> @ <custom_error>)]</code>
412/// </td>
413/// <td>
414/// Constraint that checks whether the given expression evaluates to true.<br>
415/// Use this when no other constraint fits your use case.
416/// <br><br>
417/// Example:
418/// <pre><code>
419/// #[account(constraint = one.keys[0].age == two.apple.age)]
420/// pub one: Account<'info, MyData>,
421/// pub two: Account<'info, OtherData>
422/// </code></pre>
423/// </td>
424/// </tr>
425/// <tr>
426/// <td>
427/// <code>#[account(realloc = <space>, realloc::payer = <target>, realloc::zero = <bool>)]</code>
428/// </td>
429/// <td>
430/// Used to <a href="https://docs.rs/solana-program/latest/rialo_s_program/account_info/struct.AccountInfo.html#method.realloc" target = "_blank" rel = "noopener noreferrer">realloc</a>
431/// program account space at the beginning of an instruction.
432/// <br><br>
433/// The account must be marked as <code>mut</code> and applied to either <code>Account</code> or <code>AccountLoader</code> types.
434/// <br><br>
435/// If the change in account data length is additive, kelvins will be transferred from the <code>realloc::payer</code> into the
436/// program account in order to maintain rent exemption. Likewise, if the change is subtractive, kelvins will be transferred from
437/// the program account back into the <code>realloc::payer</code>.
438/// <br><br>
439/// The <code>realloc::zero</code> constraint is required in order to determine whether the new memory should be zero initialized after
440/// reallocation. Please read the documentation on the <code>AccountInfo::realloc</code> function linked above to understand the
441/// caveats regarding compute units when providing <code>true</code> or <code>false</code> to this flag.
442/// <br><br>
443/// The manual use of `AccountInfo::realloc` is discouraged in favor of the `realloc` constraint group due to the lack of native runtime checks
444/// to prevent reallocation over the `MAX_PERMITTED_DATA_INCREASE` limit (which can unintentionally cause account data overwrite other accounts).
445/// The constraint group also ensure account reallocation idempotency but checking and restricting duplicate account reallocation within a single ix.
446/// <br><br>
447/// Example:
448/// <pre>
449/// #[derive(Accounts)]
450/// pub struct Example {
451/// #[account(mut)]
452/// pub payer: Signer<'info>,
453/// #[account(
454/// mut,
455/// seeds = [b"example"],
456/// bump,
457/// realloc = 8 + `std::mem::size_of::<MyType>`() + 100,
458/// realloc::payer = payer,
459/// realloc::zero = false,
460/// )]
461/// pub acc: Account<'info, MyType>,
462/// pub system_program: Program<'info, System>,
463/// }
464/// </pre>
465/// </td>
466/// </tr>
467/// </tbody>
468/// </table>
469///
470/// # SPL Constraints
471///
472/// Anchor provides constraints that make verifying SPL accounts easier.
473///
474/// <table>
475/// <thead>
476/// <tr>
477/// <th>Attribute</th>
478/// <th>Description</th>
479/// </tr>
480/// </thead>
481/// <tbody>
482/// <tr>
483/// <td>
484/// <code>#[account(token::mint = <target_account>, token::authority = <target_account>)]</code>
485/// <br><br>
486/// <code>#[account(token::mint = <target_account>, token::authority = <target_account>, token::token_program = <target_account>)]</code>
487/// </td>
488/// <td>
489/// Can be used as a check or with <code>init</code> to create a token
490/// account with the given mint address and authority.<br>
491/// When used as a check, it's possible to only specify a subset of the constraints.
492/// <br><br>
493/// Example:
494/// <pre>
495/// use anchor_spl::{mint, token::{TokenAccount, Mint, Token}};
496/// ...
497/// #[account(
498/// init,
499/// payer = payer,
500/// token::mint = mint,
501/// token::authority = payer,
502/// )]
503/// pub token: Account<'info, TokenAccount>,
504/// #[account(address = mint::USDC)]
505/// pub mint: Account<'info, Mint>,
506/// #[account(mut)]
507/// pub payer: Signer<'info>,
508/// pub token_program: Program<'info, Token>,
509/// pub system_program: Program<'info, System>
510/// </pre>
511/// </td>
512/// </tr>
513/// <tr>
514/// <td>
515/// <code>#[account(mint::authority = <target_account>, mint::decimals = <expr>)]</code>
516/// <br><br>
517/// <code>#[account(mint::authority = <target_account>, mint::decimals = <expr>, mint::freeze_authority = <target_account>)]</code>
518/// </td>
519/// <td>
520/// Can be used as a check or with <code>init</code> to create a mint
521/// account with the given mint decimals and mint authority.<br>
522/// The freeze authority is optional when used with <code>init</code>.<br>
523/// When used as a check, it's possible to only specify a subset of the constraints.
524/// <br><br>
525/// Example:
526/// <pre>
527/// use anchor_spl::token::{Mint, Token};
528/// ...
529/// #[account(
530/// init,
531/// payer = payer,
532/// mint::decimals = 9,
533/// mint::authority = payer,
534/// )]
535/// pub mint_one: Account<'info, Mint>,
536/// #[account(
537/// init,
538/// payer = payer,
539/// mint::decimals = 9,
540/// mint::authority = payer,
541/// mint::freeze_authority = payer
542/// )]
543/// pub mint_two: Account<'info, Mint>,
544/// #[account(mut)]
545/// pub payer: Signer<'info>,
546/// pub token_program: Program<'info, Token>,
547/// pub system_program: Program<'info, System>
548/// </pre>
549/// </td>
550/// </tr>
551/// <tr>
552/// <td>
553/// <code>#[account(associated_token::mint = <target_account>, associated_token::authority = <target_account>)]</code>
554/// <br><br>
555/// <code>#[account(associated_token::mint = <target_account>, associated_token::authority = <target_account>, associated_token::token_program = <target_account>)]</code>
556/// </td>
557/// <td>
558/// Can be used as a standalone as a check or with <code>init</code> to create an associated token
559/// account with the given mint address and authority.
560/// <br><br>
561/// Example:
562/// <pre>
563/// use anchor_spl::{
564/// associated_token::AssociatedToken,
565/// mint,
566/// token::{TokenAccount, Mint, Token}
567/// };
568/// ...
569/// #[account(
570/// init,
571/// payer = payer,
572/// associated_token::mint = mint,
573/// associated_token::authority = payer,
574/// )]
575/// pub token: Account<'info, TokenAccount>,
576/// #[account(
577/// associated_token::mint = mint,
578/// associated_token::authority = payer,
579/// )]
580/// pub second_token: Account<'info, TokenAccount>,
581/// #[account(address = mint::USDC)]
582/// pub mint: Account<'info, Mint>,
583/// #[account(mut)]
584/// pub payer: Signer<'info>,
585/// pub token_program: Program<'info, Token>,
586/// pub associated_token_program: Program<'info, AssociatedToken>,
587/// pub system_program: Program<'info, System>
588/// </pre>
589/// </td>
590/// </tr><tr>
591/// <td>
592/// <code>#[account(*::token_program = <target_account>)]</code>
593/// </td>
594/// <td>
595/// The <code>token_program</code> can optionally be overridden.
596/// <br><br>
597/// Example:
598/// <pre>
599/// use anchor_spl::token_interface::{TokenInterface, TokenAccount, Mint};
600/// ...
601/// #[account(
602/// mint::token_program = token_a_token_program,
603/// )]
604/// pub token_a_mint: InterfaceAccount<'info, Mint>,
605/// #[account(
606/// mint::token_program = token_b_token_program,
607/// )]
608/// pub token_b_mint: InterfaceAccount<'info, Mint>,
609/// #[account(
610/// init,
611/// payer = payer,
612/// token::mint = token_a_mint,
613/// token::authority = payer,
614/// token::token_program = token_a_token_program,
615/// )]
616/// pub token_a_account: InterfaceAccount<'info, TokenAccount>,
617/// #[account(
618/// init,
619/// payer = payer,
620/// token::mint = token_b_mint,
621/// token::authority = payer,
622/// token::token_program = token_b_token_program,
623/// )]
624/// pub token_b_account: InterfaceAccount<'info, TokenAccount>,
625/// pub token_a_token_program: Interface<'info, TokenInterface>,
626/// pub token_b_token_program: Interface<'info, TokenInterface>,
627/// #[account(mut)]
628/// pub payer: Signer<'info>,
629/// pub system_program: Program<'info, System>
630/// </pre>
631/// </td>
632/// </tr>
633/// </tbody>
634/// </table>
635#[proc_macro_derive(Accounts, attributes(account, instruction))]
636pub fn derive_accounts(item: TokenStream) -> TokenStream {
637 parse_macro_input!(item as rialo_sol_syn::AccountsStruct)
638 .to_token_stream()
639 .into()
640}