light_compressed_token/instructions/
create_token_pool.rs1use account_compression::utils::constants::CPI_AUTHORITY_PDA_SEED;
2use anchor_lang::prelude::*;
3use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
4use spl_token_2022::{
5 extension::{BaseStateWithExtensions, ExtensionType, PodStateWithExtensions},
6 pod::PodMint,
7};
8
9use crate::{
10 constants::{NUM_MAX_POOL_ACCOUNTS, POOL_SEED},
11 spl_compression::is_valid_token_pool_pda,
12};
13
14#[derive(Accounts)]
16pub struct CreateTokenPoolInstruction<'info> {
17 #[account(mut)]
19 pub fee_payer: Signer<'info>,
20 #[account(
21 init,
22 seeds = [
23 POOL_SEED, &mint.key().to_bytes(),
24 ],
25 bump,
26 payer = fee_payer,
27 token::mint = mint,
28 token::authority = cpi_authority_pda,
29 )]
30 pub token_pool_pda: InterfaceAccount<'info, TokenAccount>,
31 pub system_program: Program<'info, System>,
32 #[account(mut)]
34 pub mint: InterfaceAccount<'info, Mint>,
35 pub token_program: Interface<'info, TokenInterface>,
36 #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump)]
38 pub cpi_authority_pda: AccountInfo<'info>,
39}
40
41pub fn get_token_pool_pda(mint: &Pubkey) -> Pubkey {
42 get_token_pool_pda_with_index(mint, 0)
43}
44
45pub fn find_token_pool_pda_with_index(mint: &Pubkey, token_pool_index: u8) -> (Pubkey, u8) {
46 let seeds = &[POOL_SEED, mint.as_ref(), &[token_pool_index]];
47 let seeds = if token_pool_index == 0 {
48 &seeds[..2]
49 } else {
50 &seeds[..]
51 };
52 Pubkey::find_program_address(seeds, &crate::ID)
53}
54
55pub fn get_token_pool_pda_with_index(mint: &Pubkey, token_pool_index: u8) -> Pubkey {
56 find_token_pool_pda_with_index(mint, token_pool_index).0
57}
58
59const ALLOWED_EXTENSION_TYPES: [ExtensionType; 7] = [
60 ExtensionType::MetadataPointer,
61 ExtensionType::TokenMetadata,
62 ExtensionType::InterestBearingConfig,
63 ExtensionType::GroupPointer,
64 ExtensionType::GroupMemberPointer,
65 ExtensionType::TokenGroup,
66 ExtensionType::TokenGroupMember,
67];
68
69pub fn assert_mint_extensions(account_data: &[u8]) -> Result<()> {
70 let mint = PodStateWithExtensions::<PodMint>::unpack(account_data).unwrap();
71 let mint_extensions = mint.get_extension_types().unwrap();
72 if !mint_extensions
73 .iter()
74 .all(|item| ALLOWED_EXTENSION_TYPES.contains(item))
75 {
76 return err!(crate::ErrorCode::MintWithInvalidExtension);
77 }
78 Ok(())
79}
80
81#[derive(Accounts)]
83#[instruction(token_pool_index: u8)]
84pub struct AddTokenPoolInstruction<'info> {
85 #[account(mut)]
87 pub fee_payer: Signer<'info>,
88 #[account(
89 init,
90 seeds = [
91 POOL_SEED, &mint.key().to_bytes(), &[token_pool_index],
92 ],
93 bump,
94 payer = fee_payer,
95 token::mint = mint,
96 token::authority = cpi_authority_pda,
97 )]
98 pub token_pool_pda: InterfaceAccount<'info, TokenAccount>,
99 pub existing_token_pool_pda: InterfaceAccount<'info, TokenAccount>,
100 pub system_program: Program<'info, System>,
101 #[account(mut)]
103 pub mint: InterfaceAccount<'info, Mint>,
104 pub token_program: Interface<'info, TokenInterface>,
105 #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump)]
107 pub cpi_authority_pda: AccountInfo<'info>,
108}
109
110#[inline(always)]
113pub fn check_spl_token_pool_derivation(token_pool_pda: &Pubkey, mint: &Pubkey) -> Result<()> {
114 let mint_bytes = mint.to_bytes();
115 let is_valid = (0..NUM_MAX_POOL_ACCOUNTS).any(|i| {
116 is_valid_token_pool_pda(mint_bytes.as_slice(), token_pool_pda, &[i], None).unwrap_or(false)
117 });
118 if !is_valid {
119 err!(crate::ErrorCode::InvalidTokenPoolPda)
120 } else {
121 Ok(())
122 }
123}
124
125#[inline(always)]
126pub fn check_spl_token_pool_derivation_with_index(
127 token_pool_pda: &Pubkey,
128 mint: &Pubkey,
129 index: u8,
130 bump: Option<u8>,
131) -> Result<()> {
132 let mint_bytes = mint.to_bytes();
133 let is_valid = is_valid_token_pool_pda(mint_bytes.as_slice(), token_pool_pda, &[index], bump)?;
134 if !is_valid {
135 err!(crate::ErrorCode::InvalidTokenPoolPda)
136 } else {
137 Ok(())
138 }
139}
140
141#[cfg(test)]
142mod test {
143 use super::*;
144
145 #[test]
151 fn test_check_spl_token_pool_derivation() {
152 let mint = Pubkey::new_unique();
154 for i in 0..NUM_MAX_POOL_ACCOUNTS {
155 let valid_pda = get_token_pool_pda_with_index(&mint, i);
156 assert!(check_spl_token_pool_derivation(&valid_pda, &mint).is_ok());
157 }
158
159 let mint = Pubkey::new_unique();
161 let invalid_pda = Pubkey::new_unique();
162 assert!(check_spl_token_pool_derivation(&invalid_pda, &mint).is_err());
163
164 let mint = Pubkey::new_unique();
166 let invalid_pda = get_token_pool_pda_with_index(&mint, NUM_MAX_POOL_ACCOUNTS);
167 assert!(check_spl_token_pool_derivation(&invalid_pda, &mint).is_err());
168
169 let mint = Pubkey::new_unique();
171 let invalid_pda = get_token_pool_pda_with_index(&mint, NUM_MAX_POOL_ACCOUNTS + 1);
172 assert!(check_spl_token_pool_derivation(&invalid_pda, &mint).is_err());
173 }
174}