From 864e2f0a14d9f0743b700bb878069d9c5b50a3c2 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Fri, 15 Nov 2024 03:21:17 +0000 Subject: [PATCH] feat: token 2022 support --- Cargo.lock | 246 +- programs/compressed-token/src/burn.rs | 65 +- programs/compressed-token/src/freeze.rs | 23 + .../compressed-token/src/instructions/burn.rs | 12 +- .../src/instructions/create_token_pool.rs | 68 + .../src/instructions/freeze.rs | 9 +- .../compressed-token/src/instructions/mod.rs | 2 + .../src/instructions/transfer.rs | 8 +- programs/compressed-token/src/lib.rs | 10 + .../src/process_compress_spl_token_account.rs | 22 +- programs/compressed-token/src/process_mint.rs | 159 +- .../compressed-token/src/process_transfer.rs | 12 +- .../compressed-token/src/spl_compression.rs | 115 +- scripts/format.sh | 1 + .../compressed-token-test/tests/test.rs | 3553 +++++++++-------- .../tests/test_program_owned_trees.rs | 2 + test-utils/Cargo.toml | 1 + test-utils/src/e2e_test_env.rs | 3 + test-utils/src/spl.rs | 232 +- 19 files changed, 2687 insertions(+), 1856 deletions(-) create mode 100644 programs/compressed-token/src/instructions/create_token_pool.rs diff --git a/Cargo.lock b/Cargo.lock index 6e153cd1ce..9c85d5bd62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3832,6 +3832,7 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "spl-token", + "spl-token-2022 3.0.4", "thiserror", "tokio", ] @@ -6187,8 +6188,8 @@ dependencies = [ "solana-sdk", "spl-token", "spl-token-2022 1.0.0", - "spl-token-group-interface", - "spl-token-metadata-interface", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", "thiserror", "zstd", ] @@ -7688,7 +7689,18 @@ checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator-derive", + "spl-discriminator-derive 0.1.2", +] + +[[package]] +name = "spl-discriminator" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210101376962bb22bb13be6daea34656ea1cbc248fce2164b146e39203b55e03" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.2.0", ] [[package]] @@ -7698,7 +7710,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", - "spl-discriminator-syn", + "spl-discriminator-syn 0.1.2", + "syn 2.0.85", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn 0.2.0", "syn 2.0.85", ] @@ -7715,6 +7738,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-discriminator-syn" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.85", + "thiserror", +] + [[package]] name = "spl-memo" version = "4.0.0" @@ -7743,7 +7779,20 @@ dependencies = [ "bytemuck", "solana-program", "solana-zk-token-sdk", - "spl-program-error", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-pod" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c52d84c55efeef8edcc226743dc089d7e3888b8e3474569aa3eff152b37b9996" +dependencies = [ + "borsh 1.5.1", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.4.4", ] [[package]] @@ -7755,7 +7804,20 @@ dependencies = [ "num-derive 0.4.2", "num-traits", "solana-program", - "spl-program-error-derive", + "spl-program-error-derive 0.3.2", + "thiserror", +] + +[[package]] +name = "spl-program-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45a49acb925db68aa501b926096b2164adbdcade7a0c24152af9f0742d0a602" +dependencies = [ + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.4.1", "thiserror", ] @@ -7771,6 +7833,18 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.85", +] + [[package]] name = "spl-tlv-account-resolution" version = "0.4.0" @@ -7779,10 +7853,10 @@ checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", ] [[package]] @@ -7793,10 +7867,24 @@ checksum = "615d381f48ddd2bb3c57c7f7fb207591a2a05054639b18a62e785117dd7a8683" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fab8edfd37be5fa17c9e42c1bff86abbbaf0494b031b37957f2728ad2ff842ba" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-type-length-value 0.4.6", ] [[package]] @@ -7828,11 +7916,11 @@ dependencies = [ "solana-program", "solana-zk-token-sdk", "spl-memo", - "spl-pod", + "spl-pod 0.1.0", "spl-token", - "spl-token-metadata-interface", + "spl-token-metadata-interface 0.2.0", "spl-transfer-hook-interface 0.3.0", - "spl-type-length-value", + "spl-type-length-value 0.3.0", "thiserror", ] @@ -7851,12 +7939,36 @@ dependencies = [ "solana-security-txt", "solana-zk-token-sdk", "spl-memo", - "spl-pod", + "spl-pod 0.1.0", "spl-token", - "spl-token-group-interface", - "spl-token-metadata-interface", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", "spl-transfer-hook-interface 0.4.1", - "spl-type-length-value", + "spl-type-length-value 0.3.0", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d1b2851964e257187c0bca43a0de38d0af59192479ca01ac3e2b58b1bd95a" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.2", + "num-traits", + "num_enum 0.7.3", + "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod 0.2.5", + "spl-token", + "spl-token-group-interface 0.2.5", + "spl-token-metadata-interface 0.3.5", + "spl-transfer-hook-interface 0.6.5", + "spl-type-length-value 0.4.6", "thiserror", ] @@ -7868,9 +7980,22 @@ checksum = "b889509d49fa74a4a033ca5dae6c2307e9e918122d97e58562f5c4ffa795c75d" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "014817d6324b1e20c4bbc883e8ee30a5faa13e59d91d1b2b95df98b920150c17" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", ] [[package]] @@ -7881,10 +8006,24 @@ checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" dependencies = [ "borsh 0.10.3", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3da00495b602ebcf5d8ba8b3ecff1ee454ce4c125c9077747be49c2d62335ba" +dependencies = [ + "borsh 1.5.1", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-type-length-value 0.4.6", ] [[package]] @@ -7896,11 +8035,11 @@ dependencies = [ "arrayref", "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", "spl-tlv-account-resolution 0.4.0", - "spl-type-length-value", + "spl-type-length-value 0.3.0", ] [[package]] @@ -7912,11 +8051,27 @@ dependencies = [ "arrayref", "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", "spl-tlv-account-resolution 0.5.1", - "spl-type-length-value", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b5c08a89838e5a2931f79b17f611857f281a14a2100968a3ccef352cb7414b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-tlv-account-resolution 0.6.5", + "spl-type-length-value 0.4.6", ] [[package]] @@ -7927,9 +8082,22 @@ checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-type-length-value" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c872f93d0600e743116501eba2d53460e73a12c9a496875a42a7d70e034fe06d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", ] [[package]] diff --git a/programs/compressed-token/src/burn.rs b/programs/compressed-token/src/burn.rs index 3264eef58b..a7e2f76a26 100644 --- a/programs/compressed-token/src/burn.rs +++ b/programs/compressed-token/src/burn.rs @@ -15,6 +15,7 @@ use crate::{ get_input_compressed_accounts_with_merkle_context_and_check_signer, DelegatedTransfer, InputTokenDataWithContext, }, + spl_compression::spl_token_pool_derivation, BurnInstruction, ErrorCode, }; @@ -63,21 +64,52 @@ pub fn burn_spl_from_pool_pda<'info>( ctx: &Context<'_, '_, '_, 'info, BurnInstruction<'info>>, inputs: &CompressedTokenInstructionDataBurn, ) -> Result<()> { - let pre_token_balance = ctx.accounts.token_pool_pda.amount; - let cpi_accounts = anchor_spl::token::Burn { - mint: ctx.accounts.mint.to_account_info(), - from: ctx.accounts.token_pool_pda.to_account_info(), - authority: ctx.accounts.cpi_authority_pda.to_account_info(), - }; + if *ctx.accounts.mint.owner != *ctx.accounts.token_program.key { + return err!(crate::ErrorCode::InvalidTokenMintOwner); + } + spl_token_pool_derivation( + ctx.accounts.mint.key, + &crate::ID, + &ctx.accounts.token_pool_pda.key(), + )?; + + let pre_token_balance = TokenAccount::try_deserialize( + &mut &ctx.accounts.token_pool_pda.to_account_info().data.borrow()[..], + )? + .amount; + let signer_seeds = get_cpi_signer_seeds(); let signer_seeds_ref = &[&signer_seeds[..]]; - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - cpi_accounts, - signer_seeds_ref, - ); - anchor_spl::token::burn(cpi_ctx, inputs.burn_amount)?; + match *ctx.accounts.token_program.key { + spl_token::ID => { + let cpi_accounts = anchor_spl::token::Burn { + mint: ctx.accounts.mint.to_account_info(), + from: ctx.accounts.token_pool_pda.to_account_info(), + authority: ctx.accounts.cpi_authority_pda.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + signer_seeds_ref, + ); + anchor_spl::token::burn(cpi_ctx, inputs.burn_amount) + } + anchor_spl::token_2022::ID => { + let cpi_accounts = anchor_spl::token_2022::Burn { + mint: ctx.accounts.mint.to_account_info(), + from: ctx.accounts.token_pool_pda.to_account_info(), + authority: ctx.accounts.cpi_authority_pda.to_account_info(), + }; + let cpi_ctx = CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + signer_seeds_ref, + ); + anchor_spl::token_2022::burn(cpi_ctx, inputs.burn_amount) + } + _ => err!(crate::ErrorCode::InvalidTokenProgram), + }?; let post_token_balance = TokenAccount::try_deserialize( &mut &ctx.accounts.token_pool_pda.to_account_info().data.borrow()[..], @@ -202,6 +234,7 @@ pub mod sdk { pub mint: Pubkey, pub burn_amount: u64, pub signer_is_delegate: bool, + pub is_token_22: bool, } pub fn create_burn_instruction( @@ -250,14 +283,18 @@ pub mod sdk { .data(); let token_pool_pda = get_token_pool_pda(&inputs.mint); - + let token_program = if inputs.is_token_22 { + anchor_spl::token_2022::ID + } else { + spl_token::ID + }; let accounts = crate::accounts::BurnInstruction { fee_payer: inputs.fee_payer, authority: inputs.authority, cpi_authority_pda, mint: inputs.mint, token_pool_pda, - token_program: anchor_spl::token::ID, + token_program, light_system_program: light_system_program::ID, registered_program_pda: light_system_program::utils::get_registered_program_pda( &light_system_program::ID, diff --git a/programs/compressed-token/src/freeze.rs b/programs/compressed-token/src/freeze.rs index 22bbb3b315..ac0ea84354 100644 --- a/programs/compressed-token/src/freeze.rs +++ b/programs/compressed-token/src/freeze.rs @@ -1,4 +1,5 @@ use anchor_lang::prelude::*; +use anchor_spl::token::Mint; use light_hasher::DataHasher; use light_hasher::Poseidon; use light_system_program::{ @@ -46,6 +47,7 @@ pub fn process_freeze_or_thaw< ) -> Result<()> { let inputs: CompressedTokenInstructionDataFreeze = CompressedTokenInstructionDataFreeze::deserialize(&mut inputs.as_slice())?; + check_mint_and_freeze_authority(&ctx.accounts.mint, ctx.accounts.authority.key)?; let (compressed_input_accounts, output_compressed_accounts) = create_input_and_output_accounts_freeze_or_thaw::( &inputs, @@ -65,6 +67,27 @@ pub fn process_freeze_or_thaw< ) } +/// Checks: +/// 1. Mint account is owner token22 or spl token program. +/// 2. Mint account is correct account. +/// 3. Freeze authority is authority. +pub fn check_mint_and_freeze_authority(mint: &AccountInfo<'_>, authority: &Pubkey) -> Result<()> { + let freeze_authority = match *mint.owner { + anchor_spl::token_2022::ID | spl_token::ID => { + let mint = Mint::try_deserialize(&mut &mint.try_borrow_data()?[..])?; + Ok(mint + .freeze_authority + .ok_or(crate::ErrorCode::MintHasNoFreezeAuthority)?) + } + _ => err!(crate::ErrorCode::InvalidTokenProgram), + }?; + if freeze_authority != *authority { + err!(crate::ErrorCode::InvalidFreezeAuthority) + } else { + Ok(()) + } +} + pub fn create_input_and_output_accounts_freeze_or_thaw< const FROZEN_INPUTS: bool, const FROZEN_OUTPUTS: bool, diff --git a/programs/compressed-token/src/instructions/burn.rs b/programs/compressed-token/src/instructions/burn.rs index 7279821561..61ae755587 100644 --- a/programs/compressed-token/src/instructions/burn.rs +++ b/programs/compressed-token/src/instructions/burn.rs @@ -1,12 +1,11 @@ use account_compression::{program::AccountCompression, utils::constants::CPI_AUTHORITY_PDA_SEED}; use anchor_lang::prelude::*; -use anchor_spl::token::{Mint, Token, TokenAccount}; use light_system_program::{ program::LightSystemProgram, sdk::accounts::{InvokeAccounts, SignerAccounts}, }; -use crate::{program::LightCompressedToken, POOL_SEED}; +use crate::program::LightCompressedToken; #[derive(Accounts)] pub struct BurnInstruction<'info> { @@ -23,11 +22,12 @@ pub struct BurnInstruction<'info> { pub cpi_authority_pda: UncheckedAccount<'info>, /// CHECK: is used to burn tokens. #[account(mut)] - pub mint: Account<'info, Mint>, + pub mint: AccountInfo<'info>, /// CHECK: (seed constraint) is derived from mint account. - #[account(mut, seeds = [POOL_SEED, mint.key().as_ref()], bump)] - pub token_pool_pda: Account<'info, TokenAccount>, - pub token_program: Program<'info, Token>, + #[account(mut)] + pub token_pool_pda: AccountInfo<'info>, + /// CHECK: in burn_spl_from_pool_pda. + pub token_program: AccountInfo<'info>, pub light_system_program: Program<'info, LightSystemProgram>, /// CHECK: (account compression program). pub registered_program_pda: AccountInfo<'info>, diff --git a/programs/compressed-token/src/instructions/create_token_pool.rs b/programs/compressed-token/src/instructions/create_token_pool.rs new file mode 100644 index 0000000000..319e2553b6 --- /dev/null +++ b/programs/compressed-token/src/instructions/create_token_pool.rs @@ -0,0 +1,68 @@ +use account_compression::utils::constants::CPI_AUTHORITY_PDA_SEED; +use anchor_lang::prelude::*; +use anchor_spl::{ + token::{Mint, Token, TokenAccount}, + token_2022::Token2022, + token_interface::{Mint as Mint22, TokenAccount as Token22Account}, +}; +pub const POOL_SEED: &[u8] = b"pool"; + +/// Creates a spl token pool account which is owned by the token authority pda. +#[derive(Accounts)] +pub struct CreateTokenPoolInstruction<'info> { + /// UNCHECKED: only pays fees. + #[account(mut)] + pub fee_payer: Signer<'info>, + #[account( + init, + seeds = [ + POOL_SEED, &mint.key().to_bytes(), + ], + bump, + payer = fee_payer, + token::mint = mint, + token::authority = cpi_authority_pda, + )] + pub token_pool_pda: Account<'info, TokenAccount>, + pub system_program: Program<'info, System>, + /// CHECK: is mint account. + #[account(mut)] + pub mint: Account<'info, Mint>, + pub token_program: Program<'info, Token>, + /// CHECK: (seeds anchor constraint). + #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump)] + pub cpi_authority_pda: AccountInfo<'info>, +} + +/// Creates a token22 token pool account which is owned by the token authority pda. +#[derive(Accounts)] +pub struct CreateTokenPoolInstruction2022<'info> { + /// UNCHECKED: only pays fees. + #[account(mut)] + pub fee_payer: Signer<'info>, + #[account( + init, + seeds = [ + POOL_SEED, &mint.key().to_bytes(), + ], + bump, + payer = fee_payer, + token::mint = mint, + token::authority = cpi_authority_pda, + )] + pub token_pool_pda: InterfaceAccount<'info, Token22Account>, + pub system_program: Program<'info, System>, + /// CHECK: is mint account. + #[account(mut)] + pub mint: InterfaceAccount<'info, Mint22>, + pub token_program: Program<'info, Token2022>, + /// CHECK: (seeds anchor constraint). + #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump)] + pub cpi_authority_pda: AccountInfo<'info>, +} + +pub fn get_token_pool_pda(mint: &Pubkey) -> Pubkey { + let seeds = &[POOL_SEED, mint.as_ref()]; + let (address, _) = Pubkey::find_program_address(seeds, &crate::ID); + address +} diff --git a/programs/compressed-token/src/instructions/freeze.rs b/programs/compressed-token/src/instructions/freeze.rs index 7b6bbaf21d..002ec32951 100644 --- a/programs/compressed-token/src/instructions/freeze.rs +++ b/programs/compressed-token/src/instructions/freeze.rs @@ -1,6 +1,5 @@ use account_compression::{program::AccountCompression, utils::constants::CPI_AUTHORITY_PDA_SEED}; use anchor_lang::prelude::*; -use anchor_spl::token::Mint; use light_system_program::{ program::LightSystemProgram, sdk::accounts::{InvokeAccounts, SignerAccounts}, @@ -13,8 +12,8 @@ pub struct FreezeInstruction<'info> { /// UNCHECKED: only pays fees. #[account(mut)] pub fee_payer: Signer<'info>, - #[account(constraint= authority.key() == mint.freeze_authority.unwrap() - @ crate::ErrorCode::InvalidFreezeAuthority)] + /// CHECK: is freeze authority, in instruction with + /// check_mint_and_freeze_authority(). pub authority: Signer<'info>, /// CHECK: (seed constraint). #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump,)] @@ -32,7 +31,9 @@ pub struct FreezeInstruction<'info> { /// that this program is the signer of the cpi. pub self_program: Program<'info, LightCompressedToken>, pub system_program: Program<'info, System>, - pub mint: Account<'info, Mint>, + /// CHECK: owner and account type in instruction with + /// check_mint_and_freeze_authority(). + pub mint: AccountInfo<'info>, } impl<'info> InvokeAccounts<'info> for FreezeInstruction<'info> { diff --git a/programs/compressed-token/src/instructions/mod.rs b/programs/compressed-token/src/instructions/mod.rs index f6a4ee7727..0f548c84ce 100644 --- a/programs/compressed-token/src/instructions/mod.rs +++ b/programs/compressed-token/src/instructions/mod.rs @@ -1,8 +1,10 @@ pub mod burn; +pub mod create_token_pool; pub mod freeze; pub mod generic; pub mod transfer; pub use burn::*; +pub use create_token_pool::*; pub use freeze::*; pub use generic::*; pub use transfer::*; diff --git a/programs/compressed-token/src/instructions/transfer.rs b/programs/compressed-token/src/instructions/transfer.rs index 73ffa0ef3e..3f46eed03a 100644 --- a/programs/compressed-token/src/instructions/transfer.rs +++ b/programs/compressed-token/src/instructions/transfer.rs @@ -1,6 +1,5 @@ use account_compression::{program::AccountCompression, utils::constants::CPI_AUTHORITY_PDA_SEED}; use anchor_lang::prelude::*; -use anchor_spl::token::{Token, TokenAccount}; use light_system_program::{ self, program::LightSystemProgram, @@ -33,11 +32,12 @@ pub struct TransferInstruction<'info> { /// CHECK:(system program) used to derive cpi_authority_pda and check that /// this program is the signer of the cpi. pub self_program: Program<'info, LightCompressedToken>, + /// CHECK: derivation checked in compress or decompress function. #[account(mut)] - pub token_pool_pda: Option>, + pub token_pool_pda: Option>, #[account(mut, constraint= if token_pool_pda.is_some() {Ok(token_pool_pda.as_ref().unwrap().key() != compress_or_decompress_token_account.key())}else {err!(crate::ErrorCode::TokenPoolPdaUndefined)}? @crate::ErrorCode::IsTokenPoolPda)] - pub compress_or_decompress_token_account: Option>, - pub token_program: Option>, + pub compress_or_decompress_token_account: Option>, + pub token_program: Option>, pub system_program: Program<'info, System>, } diff --git a/programs/compressed-token/src/lib.rs b/programs/compressed-token/src/lib.rs index 6ece092e14..d24041421c 100644 --- a/programs/compressed-token/src/lib.rs +++ b/programs/compressed-token/src/lib.rs @@ -44,6 +44,12 @@ pub mod light_compressed_token { Ok(()) } + pub fn create_token_pool_2022<'info>( + _ctx: Context<'_, '_, '_, 'info, CreateTokenPoolInstruction2022<'info>>, + ) -> Result<()> { + Ok(()) + } + /// Mints tokens from an spl token mint to a list of compressed accounts. /// Minted tokens are transferred to a pool account owned by the compressed /// token program. The instruction creates one compressed output account for @@ -200,4 +206,8 @@ pub enum ErrorCode { InvalidTokenPoolPda, NoInputTokenAccountsProvided, NoInputsProvided, + InvalidMintAccount, + MintHasNoFreezeAuthority, + InvalidTokenProgram, + InvalidTokenMintOwner, } diff --git a/programs/compressed-token/src/process_compress_spl_token_account.rs b/programs/compressed-token/src/process_compress_spl_token_account.rs index dbcbcdb3b3..d6cfcc7ff0 100644 --- a/programs/compressed-token/src/process_compress_spl_token_account.rs +++ b/programs/compressed-token/src/process_compress_spl_token_account.rs @@ -6,6 +6,7 @@ use crate::{ ErrorCode, }; use anchor_lang::prelude::*; +use anchor_lang::solana_program::program_pack::Pack; use light_system_program::sdk::CompressedCpiContext; pub fn process_compress_spl_token_account<'info>( @@ -16,7 +17,18 @@ pub fn process_compress_spl_token_account<'info>( ) -> Result<()> { let compression_token_account = if let Some(token_account) = ctx.accounts.compress_or_decompress_token_account.as_ref() { - token_account + if *token_account.owner + != ctx + .accounts + .token_program + .as_ref() + .ok_or(ErrorCode::InvalidTokenProgram)? + .key() + { + msg!("Token account is not owned by the token program."); + return err!(ErrorCode::InvalidTokenProgram); + } + spl_token::state::Account::unpack(&token_account.data.borrow())? } else { return err!(ErrorCode::CompressedPdaUndefinedForCompress); }; @@ -66,6 +78,7 @@ pub mod sdk { mint: &Pubkey, output_merkle_tree: &Pubkey, token_account: &Pubkey, + is_token_22: bool, ) -> Instruction { let instruction_data = crate::instruction::CompressSplTokenAccount { owner: *owner, @@ -74,6 +87,11 @@ pub mod sdk { }; let (cpi_authority_pda, _) = crate::process_transfer::get_cpi_authority_pda(); let token_pool_pda = get_token_pool_pda(mint); + let token_program = if is_token_22 { + Some(anchor_spl::token_2022::ID) + } else { + Some(TokenProgramId) + }; let accounts = crate::accounts::TransferInstruction { fee_payer: *fee_payer, @@ -93,7 +111,7 @@ pub mod sdk { self_program: crate::ID, token_pool_pda: Some(token_pool_pda), compress_or_decompress_token_account: Some(*token_account), - token_program: Some(TokenProgramId), + token_program, system_program: solana_sdk::system_program::ID, }; let remaining_accounts = vec![AccountMeta::new(*output_merkle_tree, false)]; diff --git a/programs/compressed-token/src/process_mint.rs b/programs/compressed-token/src/process_mint.rs index 332f481f05..32e372eb79 100644 --- a/programs/compressed-token/src/process_mint.rs +++ b/programs/compressed-token/src/process_mint.rs @@ -1,10 +1,11 @@ use account_compression::{program::AccountCompression, utils::constants::CPI_AUTHORITY_PDA_SEED}; use anchor_lang::prelude::*; -use anchor_spl::token::{Mint, Token, TokenAccount}; +use anchor_spl::{token::TokenAccount, token_2022}; -use crate::program::LightCompressedToken; use light_system_program::{program::LightSystemProgram, OutputCompressedAccountWithPackedContext}; +use crate::{program::LightCompressedToken, spl_compression::spl_token_pool_derivation}; + #[cfg(target_os = "solana")] use { crate::process_transfer::create_output_compressed_accounts, @@ -13,35 +14,6 @@ use { light_utils::hash_to_bn254_field_size_be, }; -pub const POOL_SEED: &[u8] = b"pool"; - -/// creates a token pool account which is owned by the token authority pda -#[derive(Accounts)] -pub struct CreateTokenPoolInstruction<'info> { - /// UNCHECKED: only pays fees. - #[account(mut)] - pub fee_payer: Signer<'info>, - #[account( - init, - seeds = [ - POOL_SEED, &mint.key().to_bytes(), - ], - bump, - payer = fee_payer, - token::mint = mint, - token::authority = cpi_authority_pda, - )] - pub token_pool_pda: Account<'info, TokenAccount>, - pub system_program: Program<'info, System>, - /// CHECK: is mint account. - #[account(mut)] - pub mint: Account<'info, Mint>, - pub token_program: Program<'info, Token>, - /// CHECK: (seeds anchor constraint). - #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump)] - pub cpi_authority_pda: AccountInfo<'info>, -} - /// Mints tokens from an spl token mint to a list of compressed accounts and /// stores minted tokens in spl token pool account. /// @@ -55,8 +27,8 @@ pub struct CreateTokenPoolInstruction<'info> { /// pre_compressed_acounts_pos. /// 5. Invoke system program to execute the compressed transaction. #[allow(unused_variables)] -pub fn process_mint_to<'info>( - ctx: Context<'_, '_, '_, 'info, MintToInstruction<'info>>, +pub fn process_mint_to( + ctx: Context, recipient_pubkeys: Vec, amounts: Vec, lamports: Option, @@ -97,17 +69,16 @@ pub fn process_mint_to<'info>( mint_spl_to_pool_pda(&ctx, &amounts)?; bench_sbf_end!("tm_mint_spl_to_pool_pda"); - let hashed_mint = - hash_to_bn254_field_size_be(ctx.accounts.mint.to_account_info().key().as_ref()) - .unwrap() - .0; + let hashed_mint = hash_to_bn254_field_size_be(ctx.accounts.mint.key().as_ref()) + .unwrap() + .0; bench_sbf_start!("tm_output_compressed_accounts"); let mut output_compressed_accounts = vec![OutputCompressedAccountWithPackedContext::default(); recipient_pubkeys.len()]; let lamports_vec = lamports.map(|_| vec![lamports; amounts.len()]); create_output_compressed_accounts( &mut output_compressed_accounts, - ctx.accounts.mint.to_account_info().key(), + ctx.accounts.mint.key(), recipient_pubkeys.as_slice(), None, None, @@ -144,7 +115,7 @@ pub fn process_mint_to<'info>( #[cfg(target_os = "solana")] #[inline(never)] pub fn cpi_execute_compressed_transaction_mint_to<'info>( - ctx: &Context<'_, '_, '_, 'info, MintToInstruction<'info>>, + ctx: &Context<'_, '_, '_, 'info, MintToInstruction>, output_compressed_accounts: Vec, inputs: &mut Vec, pre_compressed_acounts_pos: usize, @@ -304,29 +275,48 @@ pub fn serialize_mint_to_cpi_instruction_data( } #[inline(never)] -pub fn mint_spl_to_pool_pda<'info>( - ctx: &Context<'_, '_, '_, 'info, MintToInstruction<'info>>, - amounts: &[u64], -) -> Result<()> { +pub fn mint_spl_to_pool_pda(ctx: &Context, amounts: &[u64]) -> Result<()> { + spl_token_pool_derivation( + &ctx.accounts.mint.key(), + &crate::ID, + &ctx.accounts.token_pool_pda.key(), + )?; let mut mint_amount: u64 = 0; for amount in amounts.iter() { mint_amount = mint_amount .checked_add(*amount) .ok_or(crate::ErrorCode::MintTooLarge)?; } - let pre_token_balance = ctx.accounts.token_pool_pda.amount; - let cpi_accounts = anchor_spl::token::MintTo { - mint: ctx.accounts.mint.to_account_info(), - to: ctx.accounts.token_pool_pda.to_account_info(), - authority: ctx.accounts.authority.to_account_info(), - }; - let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts); + let pre_token_balance = + TokenAccount::try_deserialize(&mut &ctx.accounts.token_pool_pda.data.borrow()[..])?.amount; + match ctx.accounts.token_program.key() { + spl_token::ID => { + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + anchor_spl::token::MintTo { + mint: ctx.accounts.mint.to_account_info(), + to: ctx.accounts.token_pool_pda.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), + }, + ); + anchor_spl::token::mint_to(cpi_ctx, mint_amount) + } + token_2022::ID => { + let cpi_ctx = CpiContext::new( + ctx.accounts.token_program.to_account_info(), + anchor_spl::token_2022::MintTo { + mint: ctx.accounts.mint.to_account_info(), + to: ctx.accounts.token_pool_pda.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), + }, + ); + anchor_spl::token_2022::mint_to(cpi_ctx, mint_amount) + } + _ => err!(crate::ErrorCode::InvalidTokenProgram), + }?; - anchor_spl::token::mint_to(cpi_ctx, mint_amount)?; - let post_token_balance = TokenAccount::try_deserialize( - &mut &ctx.accounts.token_pool_pda.to_account_info().data.borrow()[..], - )? - .amount; + let post_token_balance = + TokenAccount::try_deserialize(&mut &ctx.accounts.token_pool_pda.data.borrow()[..])?.amount; // Guard against unexpected behavior of the SPL token program. if post_token_balance != pre_token_balance + mint_amount { msg!( @@ -350,18 +340,15 @@ pub struct MintToInstruction<'info> { /// CHECK: #[account(seeds = [CPI_AUTHORITY_PDA_SEED], bump)] pub cpi_authority_pda: UncheckedAccount<'info>, - /// CHECK: that authority is mint authority - #[account( - mut, - constraint = mint.mint_authority.unwrap() == authority.key() - @ crate::ErrorCode::InvalidAuthorityMint - )] - pub mint: Account<'info, Mint>, - /// CHECK: this account is checked implictly since a mint to from a mint - /// account to a token account of a different mint will fail - #[account(mut, seeds = [POOL_SEED, &mint.key().to_bytes()],bump)] - pub token_pool_pda: Account<'info, TokenAccount>, - pub token_program: Program<'info, Token>, + /// CHECK: by the token program token program internal checks, since the + /// mint is written to and we check the program id we invoke to be either + /// spl_token::ID or token_2022::ID. + #[account(mut)] + pub mint: AccountInfo<'info>, + /// CHECK: with spl_token_pool_derivation(). + #[account(mut)] + pub token_pool_pda: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, pub light_system_program: Program<'info, LightSystemProgram>, /// CHECK: (different program) checked in account compression program pub registered_program_pda: UncheckedAccount<'info>, @@ -384,12 +371,6 @@ pub struct MintToInstruction<'info> { pub sol_pool_pda: Option>, } -pub fn get_token_pool_pda(mint: &Pubkey) -> Pubkey { - let seeds = &[POOL_SEED, mint.as_ref()]; - let (address, _) = Pubkey::find_program_address(seeds, &crate::ID); - address -} - #[cfg(not(target_os = "solana"))] pub mod mint_sdk { use crate::{get_token_pool_pda, process_transfer::get_cpi_authority_pda}; @@ -417,6 +398,30 @@ pub mod mint_sdk { } } + pub fn create_create_token_pool_2022_instruction( + fee_payer: &Pubkey, + mint: &Pubkey, + ) -> Instruction { + let token_pool_pda = get_token_pool_pda(mint); + let instruction_data = crate::instruction::CreateTokenPool2022 {}; + + let accounts = crate::accounts::CreateTokenPoolInstruction2022 { + fee_payer: *fee_payer, + token_pool_pda, + system_program: system_program::ID, + mint: *mint, + token_program: anchor_spl::token_2022::ID, + cpi_authority_pda: get_cpi_authority_pda().0, + }; + + Instruction { + program_id: crate::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + } + } + + #[allow(clippy::too_many_arguments)] pub fn create_mint_to_instruction( fee_payer: &Pubkey, authority: &Pubkey, @@ -425,6 +430,7 @@ pub mod mint_sdk { amounts: Vec, public_keys: Vec, lamports: Option, + token_2022: bool, ) -> Instruction { let token_pool_pda = get_token_pool_pda(mint); @@ -438,6 +444,11 @@ pub mod mint_sdk { } else { None }; + let token_program = if token_2022 { + anchor_spl::token_2022::ID + } else { + anchor_spl::token::ID + }; let accounts = crate::accounts::MintToInstruction { fee_payer: *fee_payer, @@ -445,7 +456,7 @@ pub mod mint_sdk { cpi_authority_pda: get_cpi_authority_pda().0, mint: *mint, token_pool_pda, - token_program: anchor_spl::token::ID, + token_program, light_system_program: light_system_program::ID, registered_program_pda: light_system_program::utils::get_registered_program_pda( &light_system_program::ID, diff --git a/programs/compressed-token/src/process_transfer.rs b/programs/compressed-token/src/process_transfer.rs index d08708c7c9..c0d2153e76 100644 --- a/programs/compressed-token/src/process_transfer.rs +++ b/programs/compressed-token/src/process_transfer.rs @@ -592,7 +592,7 @@ pub mod transfer_sdk { use std::collections::HashMap; use anchor_lang::{AnchorSerialize, Id, InstructionData, ToAccountMetas}; - use anchor_spl::token::Token; + use anchor_spl::{token::Token, token_2022::Token2022}; use light_system_program::{ invoke::processor::CompressedProof, sdk::compressed_account::{CompressedAccount, MerkleContext, PackedMerkleContext}, @@ -641,6 +641,7 @@ pub mod transfer_sdk { sort: bool, delegate_change_account_index: Option, lamports_change_account_merkle_tree: Option, + is_token_22: bool, ) -> Result { let (remaining_accounts, mut inputs_struct) = create_inputs_and_remaining_accounts( input_token_data, @@ -673,6 +674,13 @@ pub mod transfer_sdk { } else { *owner }; + let token_program = if compress_or_decompress_token_account.is_none() { + None + } else if is_token_22 { + Some(Token2022::id()) + } else { + Some(Token::id()) + }; let accounts = crate::accounts::TransferInstruction { fee_payer: *fee_payer, @@ -692,7 +700,7 @@ pub mod transfer_sdk { self_program: crate::ID, token_pool_pda, compress_or_decompress_token_account, - token_program: token_pool_pda.map(|_| Token::id()), + token_program, system_program: solana_sdk::system_program::ID, }; diff --git a/programs/compressed-token/src/spl_compression.rs b/programs/compressed-token/src/spl_compression.rs index a0401c3c62..669b322eeb 100644 --- a/programs/compressed-token/src/spl_compression.rs +++ b/programs/compressed-token/src/spl_compression.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use anchor_lang::{prelude::*, solana_program::account_info::AccountInfo}; use anchor_spl::token::Transfer; @@ -6,9 +7,9 @@ use crate::{ TransferInstruction, POOL_SEED, }; -pub fn process_compression_or_decompression<'info>( +pub fn process_compression_or_decompression( inputs: &CompressedTokenInstructionDataTransfer, - ctx: &Context<'_, '_, '_, 'info, TransferInstruction<'info>>, + ctx: &Context<'_, '_, '_, '_, TransferInstruction>, ) -> Result<()> { if inputs.is_compress { compress_spl_tokens(inputs, ctx) @@ -31,9 +32,9 @@ pub fn spl_token_pool_derivation( } } -pub fn decompress_spl_tokens<'info>( +pub fn decompress_spl_tokens( inputs: &CompressedTokenInstructionDataTransfer, - ctx: &Context<'_, '_, '_, 'info, TransferInstruction<'info>>, + ctx: &Context<'_, '_, '_, '_, TransferInstruction>, ) -> Result<()> { let recipient = match ctx.accounts.compress_or_decompress_token_account.as_ref() { Some(compression_recipient) => compression_recipient.to_account_info(), @@ -49,25 +50,31 @@ pub fn decompress_spl_tokens<'info>( Some(amount) => amount, None => return err!(crate::ErrorCode::DeCompressAmountUndefinedForDecompress), }; + let is_token_22 = match ctx.accounts.token_program.as_ref().unwrap().key() { + spl_token::ID => Ok(false), + anchor_spl::token_2022::ID => Ok(true), + _ => err!(crate::ErrorCode::InvalidTokenProgram), + }?; transfer( - &token_pool_pda, - &recipient, - &ctx.accounts.cpi_authority_pda.to_account_info(), - &ctx.accounts + token_pool_pda, + recipient, + ctx.accounts.cpi_authority_pda.to_account_info(), + ctx.accounts .token_program .as_ref() .unwrap() .to_account_info(), amount, + is_token_22, ) } -pub fn compress_spl_tokens<'info>( +pub fn compress_spl_tokens( inputs: &CompressedTokenInstructionDataTransfer, - ctx: &Context<'_, '_, '_, 'info, TransferInstruction<'info>>, + ctx: &Context<'_, '_, '_, '_, TransferInstruction>, ) -> Result<()> { let recipient_token_pool = match ctx.accounts.token_pool_pda.as_ref() { - Some(token_pool_pda) => token_pool_pda.to_account_info(), + Some(token_pool_pda) => token_pool_pda, None => return err!(crate::ErrorCode::CompressedPdaUndefinedForCompress), }; spl_token_pool_derivation(&inputs.mint, &crate::ID, &recipient_token_pool.key())?; @@ -76,54 +83,86 @@ pub fn compress_spl_tokens<'info>( None => return err!(crate::ErrorCode::DeCompressAmountUndefinedForCompress), }; + let is_token_22 = match ctx.accounts.token_program.as_ref().unwrap().key() { + spl_token::ID => Ok(false), + anchor_spl::token_2022::ID => Ok(true), + _ => err!(crate::ErrorCode::InvalidTokenProgram), + }?; transfer_compress( - &ctx.accounts + ctx.accounts .compress_or_decompress_token_account .as_ref() .unwrap() .to_account_info(), - &recipient_token_pool, - &ctx.accounts.authority.to_account_info(), - &ctx.accounts + recipient_token_pool.to_account_info(), + ctx.accounts.authority.to_account_info(), + ctx.accounts .token_program .as_ref() .unwrap() .to_account_info(), amount, + is_token_22, ) } pub fn transfer<'info>( - from: &AccountInfo<'info>, - to: &AccountInfo<'info>, - authority: &AccountInfo<'info>, - token_program: &AccountInfo<'info>, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + token_program: AccountInfo<'info>, amount: u64, + token_22: bool, ) -> Result<()> { let signer_seeds = get_cpi_signer_seeds(); let signer_seeds_ref = &[&signer_seeds[..]]; - let accounts = Transfer { - from: from.to_account_info(), - to: to.to_account_info(), - authority: authority.to_account_info(), - }; - let cpi_ctx = - CpiContext::new_with_signer(token_program.to_account_info(), accounts, signer_seeds_ref); - anchor_spl::token::transfer(cpi_ctx, amount) + + if token_22 { + let accounts = anchor_spl::token_2022::Transfer { + from, + to, + authority, + }; + let cpi_ctx = CpiContext::new_with_signer( + token_program.to_account_info(), + accounts, + signer_seeds_ref, + ); + anchor_spl::token_2022::transfer(cpi_ctx, amount) + } else { + let accounts = Transfer { + from, + to, + authority, + }; + let cpi_ctx = CpiContext::new_with_signer(token_program, accounts, signer_seeds_ref); + anchor_spl::token::transfer(cpi_ctx, amount) + } } pub fn transfer_compress<'info>( - from: &AccountInfo<'info>, - to: &AccountInfo<'info>, - authority: &AccountInfo<'info>, - token_program: &AccountInfo<'info>, + from: AccountInfo<'info>, + to: AccountInfo<'info>, + authority: AccountInfo<'info>, + token_program: AccountInfo<'info>, amount: u64, + token_22: bool, ) -> Result<()> { - let accounts = Transfer { - from: from.to_account_info(), - to: to.to_account_info(), - authority: authority.to_account_info(), - }; - let cpi_ctx = CpiContext::new(token_program.to_account_info(), accounts); - anchor_spl::token::transfer(cpi_ctx, amount) + if token_22 { + let accounts = anchor_spl::token_2022::Transfer { + from, + to, + authority, + }; + let cpi_ctx = CpiContext::new(token_program, accounts); + anchor_spl::token_2022::transfer(cpi_ctx, amount) + } else { + let accounts = Transfer { + from, + to, + authority, + }; + let cpi_ctx = CpiContext::new(token_program, accounts); + anchor_spl::token::transfer(cpi_ctx, amount) + } } diff --git a/scripts/format.sh b/scripts/format.sh index b5039fce59..196d5abe65 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -10,6 +10,7 @@ cargo clippy \ --workspace \ --exclude name-service \ --exclude photon-api \ + --exclude name-service \ -- -A clippy::result_large_err \ -A clippy::empty-docs \ -A clippy::to-string-trait-impl \ diff --git a/test-programs/compressed-token-test/tests/test.rs b/test-programs/compressed-token-test/tests/test.rs index ab2843e4eb..73b0b00ec7 100644 --- a/test-programs/compressed-token-test/tests/test.rs +++ b/test-programs/compressed-token-test/tests/test.rs @@ -4,37 +4,42 @@ use anchor_lang::{ system_program, AnchorDeserialize, AnchorSerialize, InstructionData, ToAccountMetas, }; use anchor_spl::token::Mint; +use anchor_spl::token_2022::spl_token_2022; use light_compressed_token::delegation::sdk::{ create_approve_instruction, create_revoke_instruction, CreateApproveInstructionInputs, CreateRevokeInstructionInputs, }; use light_compressed_token::freeze::sdk::{create_instruction, CreateInstructionInputs}; use light_compressed_token::get_token_pool_pda; -use light_compressed_token::mint_sdk::create_create_token_pool_instruction; use light_compressed_token::mint_sdk::create_mint_to_instruction; +use light_compressed_token::mint_sdk::{ + create_create_token_pool_2022_instruction, create_create_token_pool_instruction, +}; use light_compressed_token::process_transfer::transfer_sdk::create_transfer_instruction; use light_compressed_token::process_transfer::{get_cpi_authority_pda, TokenTransferOutputData}; use light_compressed_token::token_data::AccountState; use light_compressed_token::{token_data::TokenData, ErrorCode}; -use light_prover_client::gnark::helpers::{kill_prover, ProofType, ProverConfig}; +use light_prover_client::gnark::helpers::{kill_prover, spawn_prover, ProofType, ProverConfig}; use light_system_program::{ invoke::processor::CompressedProof, sdk::compressed_account::{CompressedAccountWithMerkleContext, MerkleContext}, }; use light_test_utils::rpc::test_rpc::ProgramTestRpcConnection; -use light_test_utils::spl::freeze_test; use light_test_utils::spl::mint_tokens_helper_with_lamports; -use light_test_utils::spl::mint_wrapped_sol; use light_test_utils::spl::revoke_test; use light_test_utils::spl::thaw_test; use light_test_utils::spl::BurnInstructionMode; -use light_test_utils::spl::{approve_test, create_token_pool}; -use light_test_utils::spl::{burn_test, mint_spl_tokens}; +use light_test_utils::spl::{approve_test, create_mint_22_helper}; +use light_test_utils::spl::{burn_test, mint_tokens_22_helper_with_lamports}; +use light_test_utils::spl::{ + compress_test, compressed_transfer_test, create_mint_helper, decompress_test, + mint_tokens_helper, +}; use light_test_utils::spl::{ - compress_test, compressed_transfer_test, create_mint_helper, create_token_account, - decompress_test, mint_tokens_helper, + compressed_transfer_22_test, create_burn_test_instruction, perform_compress_spl_token_account, }; -use light_test_utils::spl::{create_burn_test_instruction, perform_compress_spl_token_account}; +use light_test_utils::spl::{create_token_2022_account, freeze_test}; +use light_test_utils::spl::{mint_spl_tokens, mint_wrapped_sol}; use light_test_utils::{ airdrop_lamports, assert_rpc_error, create_account_instruction, Indexer, RpcConnection, RpcError, TokenDataWithContext, @@ -177,67 +182,93 @@ async fn test_failing_create_token_pool() { #[tokio::test] async fn test_wrapped_sol() { - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer = rpc.get_payer().insecure_clone(); - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { + spawn_prover( + false, + ProverConfig { run_mode: None, circuits: vec![ProofType::Inclusion], - }), + }, ) .await; - let native_mint = spl_token::native_mint::ID; - let token_account_keypair = Keypair::new(); - create_token_account(&mut rpc, &native_mint, &token_account_keypair, &payer) - .await - .unwrap(); - let amount = 1_000_000_000u64; - mint_wrapped_sol(&mut rpc, &payer, &token_account_keypair.pubkey(), amount) - .await - .unwrap(); - let fetched_token_account = rpc - .get_account(token_account_keypair.pubkey()) + // is token 22 fails with Instruction: InitializeAccount, Program log: Error: Invalid Mint line 216 + for is_token_22 in vec![false] { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let native_mint = if is_token_22 { + spl_token_2022::native_mint::ID + } else { + spl_token::native_mint::ID + }; + let token_account_keypair = Keypair::new(); + create_token_2022_account( + &mut rpc, + &native_mint, + &token_account_keypair, + &payer, + is_token_22, + ) .await - .unwrap() .unwrap(); - use anchor_lang::solana_program::program_pack::Pack; - let unpacked_token_account: spl_token::state::Account = - spl_token::state::Account::unpack(&fetched_token_account.data).unwrap(); - assert_eq!(unpacked_token_account.amount, amount); - assert_eq!(unpacked_token_account.owner, payer.pubkey()); - assert_eq!(unpacked_token_account.mint, native_mint); - assert!(unpacked_token_account.is_native.is_some()); - let instruction = create_create_token_pool_instruction(&payer.pubkey(), &native_mint); - rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + let amount = 1_000_000_000u64; + mint_wrapped_sol( + &mut rpc, + &payer, + &token_account_keypair.pubkey(), + amount, + is_token_22, + ) .await .unwrap(); + let fetched_token_account = rpc + .get_account(token_account_keypair.pubkey()) + .await + .unwrap() + .unwrap(); + use anchor_lang::solana_program::program_pack::Pack; + let unpacked_token_account: spl_token::state::Account = + spl_token::state::Account::unpack(&fetched_token_account.data).unwrap(); + assert_eq!(unpacked_token_account.amount, amount); + assert_eq!(unpacked_token_account.owner, payer.pubkey()); + assert_eq!(unpacked_token_account.mint, native_mint); + assert!(unpacked_token_account.is_native.is_some()); + let instruction = if is_token_22 { + create_create_token_pool_2022_instruction(&payer.pubkey(), &native_mint) + } else { + create_create_token_pool_instruction(&payer.pubkey(), &native_mint) + }; + rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer]) + .await + .unwrap(); - compress_test( - &payer, - &mut rpc, - &mut test_indexer, - amount, - &native_mint, - &env.merkle_tree_pubkey, - &token_account_keypair.pubkey(), - None, - ) - .await; - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey()); - decompress_test( - &payer, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - amount, - &env.merkle_tree_pubkey, - &token_account_keypair.pubkey(), - None, - ) - .await; + compress_test( + &payer, + &mut rpc, + &mut test_indexer, + amount, + &native_mint, + &env.merkle_tree_pubkey, + &token_account_keypair.pubkey(), + None, + is_token_22, + ) + .await; + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey()); + decompress_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + amount, + &env.merkle_tree_pubkey, + &token_account_keypair.pubkey(), + None, + is_token_22, + ) + .await; + } kill_prover(); } @@ -277,70 +308,112 @@ async fn test_mint_to(amounts: Vec, iterations: usize, lamports: Option::init_from_env(&payer, &env, None).await; + for is_token_22 in [false, true] { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + + let token_account_keypair = Keypair::new(); + let token_owner = payer.insecure_clone(); + airdrop_lamports(&mut rpc, &token_owner.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; - let token_account_keypair = Keypair::new(); - let token_owner = Keypair::new(); - airdrop_lamports(&mut rpc, &token_owner.pubkey(), 1_000_000_000) + create_token_2022_account( + &mut rpc, + &mint, + &token_account_keypair, + &token_owner, + is_token_22, + ) .await .unwrap(); - let mint = create_token_pool(&mut rpc, &payer, &token_owner.pubkey(), 2, None).await; - create_token_account(&mut rpc, &mint, &token_account_keypair, &token_owner) + let first_token_account_balance = 10; + mint_spl_tokens( + &mut rpc, + &mint, + &token_account_keypair.pubkey(), + &token_owner.pubkey(), + &token_owner, + first_token_account_balance, + is_token_22, + ) .await .unwrap(); - let first_token_account_balance = 10; - mint_spl_tokens( - &mut rpc, - &mint, - &token_account_keypair.pubkey(), - &token_owner.pubkey(), - &token_owner, - first_token_account_balance, - ) - .await - .unwrap(); + perform_compress_spl_token_account( + &mut rpc, + &mut test_indexer, + &payer, + &token_owner, + &mint, + &token_account_keypair.pubkey(), + &merkle_tree_pubkey, + None, + is_token_22, + ) + .await + .unwrap(); + let first_token_account_balance = 20; + mint_spl_tokens( + &mut rpc, + &mint, + &token_account_keypair.pubkey(), + &token_owner.pubkey(), + &token_owner, + first_token_account_balance, + is_token_22, + ) + .await + .unwrap(); + perform_compress_spl_token_account( + &mut rpc, + &mut test_indexer, + &payer, + &token_owner, + &mint, + &token_account_keypair.pubkey(), + &merkle_tree_pubkey, + Some(1), + is_token_22, + ) + .await + .unwrap(); + } +} - perform_compress_spl_token_account( +#[tokio::test] +async fn test_22_mint_to() { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let mint = create_mint_22_helper(&mut rpc, &payer).await; + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, - &payer, - &token_owner, - &mint, - &token_account_keypair.pubkey(), &merkle_tree_pubkey, - None, - ) - .await - .unwrap(); - let first_token_account_balance = 20; - mint_spl_tokens( - &mut rpc, - &mint, - &token_account_keypair.pubkey(), - &token_owner.pubkey(), - &token_owner, - first_token_account_balance, - ) - .await - .unwrap(); - perform_compress_spl_token_account( - &mut rpc, - &mut test_indexer, &payer, - &token_owner, &mint, - &token_account_keypair.pubkey(), - &merkle_tree_pubkey, - Some(1), + vec![1u64; 25].clone(), + vec![payer.pubkey(); 25].clone(), + None, + true, ) - .await - .unwrap(); + .await; +} +#[tokio::test] +async fn test_22_transfer() { + perform_transfer_22_test(1, 1, 12412, true).await; } #[tokio::test] @@ -406,376 +479,396 @@ async fn test_25_mint_to_zeros() { /// 11. Multiple mints which overflow the token supply over `u64::MAX`. #[tokio::test] async fn test_mint_to_failing() { - const MINTS: usize = 10; - - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer_1 = rpc.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - - let mut rng = rand::thread_rng(); - - let payer_2 = Keypair::new(); - airdrop_lamports(&mut rpc, &payer_2.pubkey(), 1_000_000_000) - .await - .unwrap(); - - let mint_1 = create_mint_helper(&mut rpc, &payer_1).await; - let mint_pool_1 = get_token_pool_pda(&mint_1); + for is_token_22 in vec![false, true] { + const MINTS: usize = 10; - let mint_2 = create_mint_helper(&mut rpc, &payer_2).await; - - // Make sure that the tokal token supply does not exceed `u64::MAX`. - let amounts: Vec = (0..MINTS) - .map(|_| rng.gen_range(0..(u64::MAX / MINTS as u64))) - .collect(); - let recipients = amounts - .iter() - .map(|_| Keypair::new().pubkey()) - .collect::>(); + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer_1 = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; - let instruction_data = light_compressed_token::instruction::MintTo { - amounts: amounts.clone(), - public_keys: recipients.clone(), - lamports: None, - }; + let mut rng = rand::thread_rng(); - // 1. Try to mint token from `mint_1` and sign the transaction with `mint_2` - // authority. - { - let instruction = create_mint_to_instruction( - &payer_2.pubkey(), - &payer_2.pubkey(), - &mint_1, - &merkle_tree_pubkey, - amounts.clone(), - recipients.clone(), - None, - ); - let result = rpc - .create_and_send_transaction(&[instruction], &payer_2.pubkey(), &[&payer_2]) - .await; - assert_rpc_error(result, 0, ErrorCode::InvalidAuthorityMint.into()).unwrap(); - } - // 2. Try to mint token from `mint_2` and sign the transaction with `mint_1` - // authority. - { - let instruction = create_mint_to_instruction( - &payer_1.pubkey(), - &payer_1.pubkey(), - &mint_2, - &merkle_tree_pubkey, - amounts.clone(), - recipients.clone(), - None, - ); - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error(result, 0, ErrorCode::InvalidAuthorityMint.into()).unwrap(); - } - // 3. Try to mint token to random token account. - { - let token_account_keypair = Keypair::new(); - create_token_account(&mut rpc, &mint_1, &token_account_keypair, &payer_1) + let payer_2 = Keypair::new(); + airdrop_lamports(&mut rpc, &payer_2.pubkey(), 1_000_000_000) .await .unwrap(); - let accounts = light_compressed_token::accounts::MintToInstruction { - fee_payer: payer_1.pubkey(), - authority: payer_1.pubkey(), - cpi_authority_pda: get_cpi_authority_pda().0, - mint: mint_1, - token_pool_pda: token_account_keypair.pubkey(), - token_program: anchor_spl::token::ID, - light_system_program: light_system_program::ID, - registered_program_pda: light_system_program::utils::get_registered_program_pda( - &light_system_program::ID, - ), - noop_program: Pubkey::new_from_array( - account_compression::utils::constants::NOOP_PUBKEY, - ), - account_compression_authority: light_system_program::utils::get_cpi_authority_pda( - &light_system_program::ID, - ), - account_compression_program: account_compression::ID, - merkle_tree: merkle_tree_pubkey, - self_program: light_compressed_token::ID, - system_program: system_program::ID, - sol_pool_pda: None, + + let mint_1 = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer_1).await + } else { + create_mint_helper(&mut rpc, &payer_1).await }; - let instruction = Instruction { - program_id: light_compressed_token::ID, - accounts: accounts.to_account_metas(Some(true)), - data: instruction_data.data(), + let mint_pool_1 = get_token_pool_pda(&mint_1); + + let mint_2 = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer_2).await + } else { + create_mint_helper(&mut rpc, &payer_2).await }; - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error( - result, - 0, - anchor_lang::error::ErrorCode::ConstraintSeeds.into(), - ) - .unwrap(); - } - // 4. Try to mint token from `mint_2` while using `mint_1` pool. - { - let accounts = light_compressed_token::accounts::MintToInstruction { - fee_payer: payer_2.pubkey(), - authority: payer_2.pubkey(), - cpi_authority_pda: get_cpi_authority_pda().0, - mint: mint_2, - token_pool_pda: mint_pool_1, - token_program: anchor_spl::token::ID, - light_system_program: light_system_program::ID, - registered_program_pda: light_system_program::utils::get_registered_program_pda( - &light_system_program::ID, - ), - noop_program: Pubkey::new_from_array( - account_compression::utils::constants::NOOP_PUBKEY, - ), - account_compression_authority: light_system_program::utils::get_cpi_authority_pda( - &light_system_program::ID, - ), - account_compression_program: account_compression::ID, - merkle_tree: merkle_tree_pubkey, - self_program: light_compressed_token::ID, - system_program: system_program::ID, - sol_pool_pda: None, + + // Make sure that the tokal token supply does not exceed `u64::MAX`. + let amounts: Vec = (0..MINTS) + .map(|_| rng.gen_range(0..(u64::MAX / MINTS as u64))) + .collect(); + let recipients = amounts + .iter() + .map(|_| Keypair::new().pubkey()) + .collect::>(); + + let instruction_data = light_compressed_token::instruction::MintTo { + amounts: amounts.clone(), + public_keys: recipients.clone(), + lamports: None, }; - let instruction = Instruction { - program_id: light_compressed_token::ID, - accounts: accounts.to_account_metas(Some(true)), - data: instruction_data.data(), + let token_program = if is_token_22 { + anchor_spl::token_2022::ID + } else { + anchor_spl::token::ID }; - let result = rpc - .create_and_send_transaction(&[instruction], &payer_2.pubkey(), &[&payer_2]) - .await; - assert_rpc_error( - result, - 0, - anchor_lang::error::ErrorCode::ConstraintSeeds.into(), - ) - .unwrap(); - } - // 5. Invalid CPI authority. - { - let invalid_cpi_authority_pda = Keypair::new(); - let accounts = light_compressed_token::accounts::MintToInstruction { - fee_payer: payer_2.pubkey(), - authority: payer_2.pubkey(), - cpi_authority_pda: invalid_cpi_authority_pda.pubkey(), - mint: mint_1, - token_pool_pda: mint_pool_1, - token_program: anchor_spl::token::ID, - light_system_program: light_system_program::ID, - registered_program_pda: light_system_program::utils::get_registered_program_pda( - &light_system_program::ID, - ), - noop_program: Pubkey::new_from_array( - account_compression::utils::constants::NOOP_PUBKEY, - ), - account_compression_authority: light_system_program::utils::get_cpi_authority_pda( - &light_system_program::ID, - ), - account_compression_program: account_compression::ID, - merkle_tree: merkle_tree_pubkey, - self_program: light_compressed_token::ID, - system_program: system_program::ID, - sol_pool_pda: None, - }; - let instruction = Instruction { - program_id: light_compressed_token::ID, - accounts: accounts.to_account_metas(Some(true)), - data: instruction_data.data(), - }; - let result = rpc - .create_and_send_transaction(&[instruction], &payer_2.pubkey(), &[&payer_2]) - .await; - assert_rpc_error( - result, - 0, - anchor_lang::error::ErrorCode::ConstraintSeeds.into(), - ) - .unwrap(); - } - // 6. Invalid registered program. - { - let invalid_registered_program = Keypair::new(); - let accounts = light_compressed_token::accounts::MintToInstruction { - fee_payer: payer_1.pubkey(), - authority: payer_1.pubkey(), - cpi_authority_pda: get_cpi_authority_pda().0, - mint: mint_1, - token_pool_pda: mint_pool_1, - token_program: anchor_spl::token::ID, - light_system_program: light_system_program::ID, - registered_program_pda: invalid_registered_program.pubkey(), - noop_program: Pubkey::new_from_array( - account_compression::utils::constants::NOOP_PUBKEY, - ), - account_compression_authority: light_system_program::utils::get_cpi_authority_pda( - &light_system_program::ID, - ), - account_compression_program: account_compression::ID, - merkle_tree: merkle_tree_pubkey, - self_program: light_compressed_token::ID, - system_program: system_program::ID, - sol_pool_pda: None, - }; - let instruction = Instruction { - program_id: light_compressed_token::ID, - accounts: accounts.to_account_metas(Some(true)), - data: instruction_data.data(), - }; - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error( - result, - 0, - anchor_lang::error::ErrorCode::ConstraintSeeds.into(), - ) - .unwrap(); - } - // 7. Invalid noop program. - { - let invalid_noop_program = Keypair::new(); - let accounts = light_compressed_token::accounts::MintToInstruction { - fee_payer: payer_1.pubkey(), - authority: payer_1.pubkey(), - cpi_authority_pda: get_cpi_authority_pda().0, - mint: mint_1, - token_pool_pda: mint_pool_1, - token_program: anchor_spl::token::ID, - light_system_program: light_system_program::ID, - registered_program_pda: light_system_program::utils::get_registered_program_pda( - &light_system_program::ID, - ), - noop_program: invalid_noop_program.pubkey(), - account_compression_authority: light_system_program::utils::get_cpi_authority_pda( - &light_system_program::ID, - ), - account_compression_program: account_compression::ID, - merkle_tree: merkle_tree_pubkey, - self_program: light_compressed_token::ID, - system_program: system_program::ID, - sol_pool_pda: None, - }; - let instruction = Instruction { - program_id: light_compressed_token::ID, - accounts: accounts.to_account_metas(Some(true)), - data: instruction_data.data(), - }; - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error( - result, - 0, - account_compression::errors::AccountCompressionErrorCode::InvalidNoopPubkey.into(), - ) - .unwrap(); - } - // 8. Invalid account compression authority. - { - let invalid_account_compression_authority = Keypair::new(); - let accounts = light_compressed_token::accounts::MintToInstruction { - fee_payer: payer_1.pubkey(), - authority: payer_1.pubkey(), - cpi_authority_pda: get_cpi_authority_pda().0, - mint: mint_1, - token_pool_pda: mint_pool_1, - token_program: anchor_spl::token::ID, - light_system_program: light_system_program::ID, - registered_program_pda: light_system_program::utils::get_registered_program_pda( - &light_system_program::ID, - ), - noop_program: Pubkey::new_from_array( - account_compression::utils::constants::NOOP_PUBKEY, - ), - account_compression_authority: invalid_account_compression_authority.pubkey(), - account_compression_program: account_compression::ID, - merkle_tree: merkle_tree_pubkey, - self_program: light_compressed_token::ID, - system_program: system_program::ID, - sol_pool_pda: None, - }; - let instruction = Instruction { - program_id: light_compressed_token::ID, - accounts: accounts.to_account_metas(Some(true)), - data: instruction_data.data(), - }; - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error( - result, - 0, - anchor_lang::error::ErrorCode::ConstraintSeeds.into(), - ) - .unwrap(); - } - // 9. Invalid Merkle tree. - { - let invalid_merkle_tree = Keypair::new(); - let instruction = create_mint_to_instruction( - &payer_1.pubkey(), - &payer_1.pubkey(), - &mint_1, - &invalid_merkle_tree.pubkey(), - amounts.clone(), - recipients.clone(), - None, - ); - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert!(matches!( - result, - Err(RpcError::TransactionError( - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) - )) - )); - } - // 10. Mint more than `u64::MAX` tokens. - { - // Overall sum greater than `u64::MAX` - let amounts = vec![u64::MAX / 5; MINTS]; - let instruction = create_mint_to_instruction( - &payer_1.pubkey(), - &payer_1.pubkey(), - &mint_1, - &merkle_tree_pubkey, - amounts, - recipients.clone(), - None, - ); - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error(result, 0, ErrorCode::MintTooLarge.into()).unwrap(); - } - // 11. Multiple mints which overflow the token supply over `u64::MAX`. - { - let amounts = vec![u64::MAX / 10; MINTS]; - let instruction = create_mint_to_instruction( - &payer_1.pubkey(), - &payer_1.pubkey(), - &mint_1, - &merkle_tree_pubkey, - amounts, - recipients.clone(), - None, - ); - // The first mint is still below `u64::MAX`. - rpc.create_and_send_transaction(&[instruction.clone()], &payer_1.pubkey(), &[&payer_1]) - .await - .unwrap(); - // The second mint should overflow. - let result = rpc - .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) - .await; - assert_rpc_error(result, 0, TokenError::Overflow as u32).unwrap(); + // 1. Try to mint token from `mint_1` and sign the transaction with `mint_2` + // authority. + { + let instruction = create_mint_to_instruction( + &payer_2.pubkey(), + &payer_2.pubkey(), + &mint_1, + &merkle_tree_pubkey, + amounts.clone(), + recipients.clone(), + None, + is_token_22, + ); + let result = rpc + .create_and_send_transaction(&[instruction], &payer_2.pubkey(), &[&payer_2]) + .await; + // Owner doesn't match the mint authority. + assert_rpc_error(result, 0, 4).unwrap(); + } + // 2. Try to mint token from `mint_2` and sign the transaction with `mint_1` + // authority. + { + let instruction = create_mint_to_instruction( + &payer_1.pubkey(), + &payer_1.pubkey(), + &mint_2, + &merkle_tree_pubkey, + amounts.clone(), + recipients.clone(), + None, + is_token_22, + ); + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + // Owner doesn't match the mint authority. + assert_rpc_error(result, 0, 4).unwrap(); + } + // 3. Try to mint token to random token account. + { + let token_account_keypair = Keypair::new(); + create_token_2022_account( + &mut rpc, + &mint_1, + &token_account_keypair, + &payer_1, + is_token_22, + ) + .await + .unwrap(); + let accounts = light_compressed_token::accounts::MintToInstruction { + fee_payer: payer_1.pubkey(), + authority: payer_1.pubkey(), + cpi_authority_pda: get_cpi_authority_pda().0, + mint: mint_1, + token_pool_pda: token_account_keypair.pubkey(), + token_program, + light_system_program: light_system_program::ID, + registered_program_pda: light_system_program::utils::get_registered_program_pda( + &light_system_program::ID, + ), + noop_program: Pubkey::new_from_array( + account_compression::utils::constants::NOOP_PUBKEY, + ), + account_compression_authority: light_system_program::utils::get_cpi_authority_pda( + &light_system_program::ID, + ), + account_compression_program: account_compression::ID, + merkle_tree: merkle_tree_pubkey, + self_program: light_compressed_token::ID, + system_program: system_program::ID, + sol_pool_pda: None, + }; + let instruction = Instruction { + program_id: light_compressed_token::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + }; + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert_rpc_error(result, 0, ErrorCode::InvalidTokenPoolPda.into()).unwrap(); + } + // 4. Try to mint token from `mint_2` while using `mint_1` pool. + { + let accounts = light_compressed_token::accounts::MintToInstruction { + fee_payer: payer_2.pubkey(), + authority: payer_2.pubkey(), + cpi_authority_pda: get_cpi_authority_pda().0, + mint: mint_2, + token_pool_pda: mint_pool_1, + token_program, + light_system_program: light_system_program::ID, + registered_program_pda: light_system_program::utils::get_registered_program_pda( + &light_system_program::ID, + ), + noop_program: Pubkey::new_from_array( + account_compression::utils::constants::NOOP_PUBKEY, + ), + account_compression_authority: light_system_program::utils::get_cpi_authority_pda( + &light_system_program::ID, + ), + account_compression_program: account_compression::ID, + merkle_tree: merkle_tree_pubkey, + self_program: light_compressed_token::ID, + system_program: system_program::ID, + sol_pool_pda: None, + }; + let instruction = Instruction { + program_id: light_compressed_token::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + }; + let result = rpc + .create_and_send_transaction(&[instruction], &payer_2.pubkey(), &[&payer_2]) + .await; + assert_rpc_error(result, 0, ErrorCode::InvalidTokenPoolPda.into()).unwrap(); + } + // 5. Invalid CPI authority. + { + let invalid_cpi_authority_pda = Keypair::new(); + let accounts = light_compressed_token::accounts::MintToInstruction { + fee_payer: payer_2.pubkey(), + authority: payer_2.pubkey(), + cpi_authority_pda: invalid_cpi_authority_pda.pubkey(), + mint: mint_1, + token_pool_pda: mint_pool_1, + token_program, + light_system_program: light_system_program::ID, + registered_program_pda: light_system_program::utils::get_registered_program_pda( + &light_system_program::ID, + ), + noop_program: Pubkey::new_from_array( + account_compression::utils::constants::NOOP_PUBKEY, + ), + account_compression_authority: light_system_program::utils::get_cpi_authority_pda( + &light_system_program::ID, + ), + account_compression_program: account_compression::ID, + merkle_tree: merkle_tree_pubkey, + self_program: light_compressed_token::ID, + system_program: system_program::ID, + sol_pool_pda: None, + }; + let instruction = Instruction { + program_id: light_compressed_token::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + }; + let result = rpc + .create_and_send_transaction(&[instruction], &payer_2.pubkey(), &[&payer_2]) + .await; + assert_rpc_error( + result, + 0, + anchor_lang::error::ErrorCode::ConstraintSeeds.into(), + ) + .unwrap(); + } + // 6. Invalid registered program. + { + let invalid_registered_program = Keypair::new(); + let accounts = light_compressed_token::accounts::MintToInstruction { + fee_payer: payer_1.pubkey(), + authority: payer_1.pubkey(), + cpi_authority_pda: get_cpi_authority_pda().0, + mint: mint_1, + token_pool_pda: mint_pool_1, + token_program, + light_system_program: light_system_program::ID, + registered_program_pda: invalid_registered_program.pubkey(), + noop_program: Pubkey::new_from_array( + account_compression::utils::constants::NOOP_PUBKEY, + ), + account_compression_authority: light_system_program::utils::get_cpi_authority_pda( + &light_system_program::ID, + ), + account_compression_program: account_compression::ID, + merkle_tree: merkle_tree_pubkey, + self_program: light_compressed_token::ID, + system_program: system_program::ID, + sol_pool_pda: None, + }; + let instruction = Instruction { + program_id: light_compressed_token::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + }; + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert_rpc_error( + result, + 0, + anchor_lang::error::ErrorCode::ConstraintSeeds.into(), + ) + .unwrap(); + } + // 7. Invalid noop program. + { + let invalid_noop_program = Keypair::new(); + let accounts = light_compressed_token::accounts::MintToInstruction { + fee_payer: payer_1.pubkey(), + authority: payer_1.pubkey(), + cpi_authority_pda: get_cpi_authority_pda().0, + mint: mint_1, + token_pool_pda: mint_pool_1, + token_program, + light_system_program: light_system_program::ID, + registered_program_pda: light_system_program::utils::get_registered_program_pda( + &light_system_program::ID, + ), + noop_program: invalid_noop_program.pubkey(), + account_compression_authority: light_system_program::utils::get_cpi_authority_pda( + &light_system_program::ID, + ), + account_compression_program: account_compression::ID, + merkle_tree: merkle_tree_pubkey, + self_program: light_compressed_token::ID, + system_program: system_program::ID, + sol_pool_pda: None, + }; + let instruction = Instruction { + program_id: light_compressed_token::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + }; + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert_rpc_error( + result, + 0, + account_compression::errors::AccountCompressionErrorCode::InvalidNoopPubkey.into(), + ) + .unwrap(); + } + // 8. Invalid account compression authority. + { + let invalid_account_compression_authority = Keypair::new(); + let accounts = light_compressed_token::accounts::MintToInstruction { + fee_payer: payer_1.pubkey(), + authority: payer_1.pubkey(), + cpi_authority_pda: get_cpi_authority_pda().0, + mint: mint_1, + token_pool_pda: mint_pool_1, + token_program, + light_system_program: light_system_program::ID, + registered_program_pda: light_system_program::utils::get_registered_program_pda( + &light_system_program::ID, + ), + noop_program: Pubkey::new_from_array( + account_compression::utils::constants::NOOP_PUBKEY, + ), + account_compression_authority: invalid_account_compression_authority.pubkey(), + account_compression_program: account_compression::ID, + merkle_tree: merkle_tree_pubkey, + self_program: light_compressed_token::ID, + system_program: system_program::ID, + sol_pool_pda: None, + }; + let instruction = Instruction { + program_id: light_compressed_token::ID, + accounts: accounts.to_account_metas(Some(true)), + data: instruction_data.data(), + }; + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert_rpc_error( + result, + 0, + anchor_lang::error::ErrorCode::ConstraintSeeds.into(), + ) + .unwrap(); + } + // 9. Invalid Merkle tree. + { + let invalid_merkle_tree = Keypair::new(); + let instruction = create_mint_to_instruction( + &payer_1.pubkey(), + &payer_1.pubkey(), + &mint_1, + &invalid_merkle_tree.pubkey(), + amounts.clone(), + recipients.clone(), + None, + is_token_22, + ); + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert!(matches!( + result, + Err(RpcError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::ProgramFailedToComplete + ) + )) + )); + } + // 10. Mint more than `u64::MAX` tokens. + { + // Overall sum greater than `u64::MAX` + let amounts = vec![u64::MAX / 5; MINTS]; + let instruction = create_mint_to_instruction( + &payer_1.pubkey(), + &payer_1.pubkey(), + &mint_1, + &merkle_tree_pubkey, + amounts, + recipients.clone(), + None, + is_token_22, + ); + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert_rpc_error(result, 0, ErrorCode::MintTooLarge.into()).unwrap(); + } + // 11. Multiple mints which overflow the token supply over `u64::MAX`. + { + let amounts = vec![u64::MAX / 10; MINTS]; + let instruction = create_mint_to_instruction( + &payer_1.pubkey(), + &payer_1.pubkey(), + &mint_1, + &merkle_tree_pubkey, + amounts, + recipients.clone(), + None, + is_token_22, + ); + // The first mint is still below `u64::MAX`. + rpc.create_and_send_transaction(&[instruction.clone()], &payer_1.pubkey(), &[&payer_1]) + .await + .unwrap(); + // The second mint should overflow. + let result = rpc + .create_and_send_transaction(&[instruction], &payer_1.pubkey(), &[&payer_1]) + .await; + assert_rpc_error(result, 0, TokenError::Overflow as u32).unwrap(); + } } } @@ -848,6 +941,9 @@ async fn test_8_transfer() { /// Creates inputs compressed accounts with amount tokens each /// Transfers all tokens from inputs compressed accounts evenly distributed to outputs compressed accounts async fn perform_transfer_test(inputs: usize, outputs: usize, amount: u64) { + perform_transfer_22_test(inputs, outputs, amount, false).await; +} +async fn perform_transfer_22_test(inputs: usize, outputs: usize, amount: u64, token_22: bool) { let (mut rpc, env) = setup_test_programs_with_accounts(None).await; let payer = rpc.get_payer().insecure_clone(); let merkle_tree_pubkey = env.merkle_tree_pubkey; @@ -860,9 +956,13 @@ async fn perform_transfer_test(inputs: usize, outputs: usize, amount: u64) { }), ) .await; - let mint = create_mint_helper(&mut rpc, &payer).await; + let mint = if token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; let sender = Keypair::new(); - mint_tokens_helper_with_lamports( + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, &merkle_tree_pubkey, @@ -871,6 +971,7 @@ async fn perform_transfer_test(inputs: usize, outputs: usize, amount: u64) { vec![amount; inputs], vec![sender.pubkey(); inputs], Some(1_000_000), + token_22, ) .await; let mut recipients = Vec::new(); @@ -883,7 +984,7 @@ async fn perform_transfer_test(inputs: usize, outputs: usize, amount: u64) { let rest_amount = (amount * inputs as u64) % outputs as u64; let mut output_amounts = vec![equal_amount; outputs - 1]; output_amounts.push(equal_amount + rest_amount); - compressed_transfer_test( + compressed_transfer_22_test( &payer, &mut rpc, &mut test_indexer, @@ -897,69 +998,92 @@ async fn perform_transfer_test(inputs: usize, outputs: usize, amount: u64) { None, false, None, + token_22, ) .await; } #[tokio::test] async fn test_decompression() { - let (mut context, env) = setup_test_programs_with_accounts(None).await; - let payer = context.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { + spawn_prover( + false, + ProverConfig { run_mode: None, circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut context, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut context, &payer).await; - let amount = 10000u64; - mint_tokens_helper( - &mut context, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![amount], - vec![sender.pubkey()], + }, ) .await; - let token_account_keypair = Keypair::new(); - create_token_account(&mut context, &mint, &token_account_keypair, &sender) + for is_token_22 in vec![false, true] { + println!("is_token_22: {}", is_token_22); + let (mut context, env) = setup_test_programs_with_accounts(None).await; + let payer = context.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut context, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut context, &payer).await + } else { + create_mint_helper(&mut context, &payer).await + }; + let amount = 10000u64; + println!("2"); + + mint_tokens_22_helper_with_lamports( + &mut context, + &mut test_indexer, + &merkle_tree_pubkey, + &payer, + &mint, + vec![amount], + vec![sender.pubkey()], + None, + is_token_22, + ) + .await; + println!("3"); + let token_account_keypair = Keypair::new(); + create_token_2022_account( + &mut context, + &mint, + &token_account_keypair, + &sender, + is_token_22, + ) .await .unwrap(); - let input_compressed_account = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - decompress_test( - &sender, - &mut context, - &mut test_indexer, - input_compressed_account, - amount, - &merkle_tree_pubkey, - &token_account_keypair.pubkey(), - None, - ) - .await; - - compress_test( - &sender, - &mut context, - &mut test_indexer, - amount, - &mint, - &merkle_tree_pubkey, - &token_account_keypair.pubkey(), - None, - ) - .await; + println!("4"); + let input_compressed_account = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + decompress_test( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account, + amount, + &merkle_tree_pubkey, + &token_account_keypair.pubkey(), + None, + is_token_22, + ) + .await; + println!("5"); + compress_test( + &sender, + &mut context, + &mut test_indexer, + amount, + &mint, + &merkle_tree_pubkey, + &token_account_keypair.pubkey(), + None, + is_token_22, + ) + .await; + } kill_prover(); } @@ -1927,711 +2051,616 @@ async fn test_revoke_failing() { /// 2. Burn delegated tokens #[tokio::test] async fn test_burn() { - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer = rpc.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { + spawn_prover( + false, + ProverConfig { run_mode: None, circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let delegate = Keypair::new(); - airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut rpc, &payer).await; - let amount = 10000u64; - mint_tokens_helper_with_lamports( - &mut rpc, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![amount], - vec![sender.pubkey()], - Some(1_000_000), + }, ) .await; - // 1. Burn tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let burn_amount = 1000u64; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - burn_test( - &sender, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - false, - None, - ) - .await; - } - // 2. Delegate tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let delegated_amount = 1000u64; - let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - approve_test( - &sender, + for is_token_22 in [false, true] { + println!("is_token_22: {}", is_token_22); + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + let delegate = Keypair::new(); + airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; + let amount = 10000u64; + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, - input_compressed_accounts, - delegated_amount, - None, - &delegate.pubkey(), - &delegated_compressed_account_merkle_tree, - &delegated_compressed_account_merkle_tree, - None, - ) - .await; - } - // 3. Burn delegated tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let input_compressed_accounts = input_compressed_accounts - .iter() - .filter(|x| x.token_data.delegate.is_some()) - .cloned() - .collect::>(); - let burn_amount = 100; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - burn_test( - &delegate, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - true, - None, - ) - .await; - } - // 3. Burn all delegated tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let input_compressed_accounts = input_compressed_accounts - .iter() - .filter(|x| x.token_data.delegate.is_some()) - .cloned() - .collect::>(); - let burn_amount = input_compressed_accounts - .iter() - .map(|x| x.token_data.amount) - .sum::(); - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - burn_test( - &delegate, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - true, - None, + &merkle_tree_pubkey, + &payer, + &mint, + vec![amount], + vec![sender.pubkey()], + Some(1_000_000), + is_token_22, ) .await; + // 1. Burn tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let burn_amount = 1000u64; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + burn_test( + &sender, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + false, + None, + is_token_22, + ) + .await; + } + // 2. Delegate tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let delegated_amount = 1000u64; + let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + approve_test( + &sender, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + delegated_amount, + None, + &delegate.pubkey(), + &delegated_compressed_account_merkle_tree, + &delegated_compressed_account_merkle_tree, + None, + ) + .await; + } + // 3. Burn delegated tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = input_compressed_accounts + .iter() + .filter(|x| x.token_data.delegate.is_some()) + .cloned() + .collect::>(); + let burn_amount = 100; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + burn_test( + &delegate, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + true, + None, + is_token_22, + ) + .await; + } + // 3. Burn all delegated tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = input_compressed_accounts + .iter() + .filter(|x| x.token_data.delegate.is_some()) + .cloned() + .collect::>(); + let burn_amount = input_compressed_accounts + .iter() + .map(|x| x.token_data.amount) + .sum::(); + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + burn_test( + &delegate, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + true, + None, + is_token_22, + ) + .await; + } } } #[tokio::test] async fn failing_tests_burn() { - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer = rpc.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { + spawn_prover( + false, + ProverConfig { run_mode: None, circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let delegate = Keypair::new(); - airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut rpc, &payer).await; - let amount = 10000u64; - mint_tokens_helper( - &mut rpc, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![amount], - vec![sender.pubkey()], + }, ) .await; - // Delegate tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let delegated_amount = 1000u64; - let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - approve_test( - &sender, + for is_token_22 in [false, true] { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + let delegate = Keypair::new(); + airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; + let amount = 10000u64; + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, - input_compressed_accounts, - delegated_amount, - None, - &delegate.pubkey(), - &delegated_compressed_account_merkle_tree, - &delegated_compressed_account_merkle_tree, + &merkle_tree_pubkey, + &payer, + &mint, + vec![amount], + vec![sender.pubkey()], None, + is_token_22, ) .await; + // Delegate tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let delegated_amount = 1000u64; + let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + approve_test( + &sender, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + delegated_amount, + None, + &delegate.pubkey(), + &delegated_compressed_account_merkle_tree, + &delegated_compressed_account_merkle_tree, + None, + ) + .await; + } + // 1. invalid proof + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let burn_amount = 1; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + let (_, _, _, _, instruction) = create_burn_test_instruction( + &sender, + &mut rpc, + &mut test_indexer, + &input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + false, + BurnInstructionMode::InvalidProof, + is_token_22, + ) + .await; + let res = rpc + .create_and_send_transaction(&[instruction], &sender.pubkey(), &[&payer, &sender]) + .await; + assert_rpc_error(res, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } + // 2. Signer is delegate but token data has no delegate. + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let burn_amount = 1; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + let (_, _, _, _, instruction) = create_burn_test_instruction( + &delegate, + &mut rpc, + &mut test_indexer, + &input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + true, + BurnInstructionMode::Normal, + is_token_22, + ) + .await; + let res = rpc + .create_and_send_transaction( + &[instruction], + &delegate.pubkey(), + &[&payer, &delegate], + ) + .await; + assert_rpc_error(res, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } + // 3. Signer is delegate but token data has no delegate. + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = input_compressed_accounts + .iter() + .filter(|x| x.token_data.delegate.is_some()) + .cloned() + .collect::>(); + let burn_amount = 1; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + let (_, _, _, _, instruction) = create_burn_test_instruction( + &sender, + &mut rpc, + &mut test_indexer, + &input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + true, + BurnInstructionMode::Normal, + is_token_22, + ) + .await; + let res = rpc + .create_and_send_transaction(&[instruction], &sender.pubkey(), &[&payer, &sender]) + .await; + assert_rpc_error(res, 0, ErrorCode::DelegateSignerCheckFailed.into()).unwrap(); + } + // 4. invalid authority (use delegate as authority) + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let burn_amount = 1; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + let (_, _, _, _, instruction) = create_burn_test_instruction( + &delegate, + &mut rpc, + &mut test_indexer, + &input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + false, + BurnInstructionMode::Normal, + is_token_22, + ) + .await; + let res = rpc + .create_and_send_transaction( + &[instruction], + &delegate.pubkey(), + &[&payer, &delegate], + ) + .await; + assert_rpc_error(res, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } + // 5. invalid mint + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let burn_amount = 1; + let change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + let (_, _, _, _, instruction) = create_burn_test_instruction( + &sender, + &mut rpc, + &mut test_indexer, + &input_compressed_accounts, + &change_account_merkle_tree, + burn_amount, + false, + BurnInstructionMode::InvalidMint, + is_token_22, + ) + .await; + let res = rpc + .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &sender]) + .await; + assert_rpc_error(res, 0, ErrorCode::InvalidTokenMintOwner.into()).unwrap(); + // assert!(matches!( + // res, + // Err(RpcError::TransactionError( + // TransactionError::InstructionError(0, InstructionError::InvalidAccountData) + // )) + // )); + } + // 6. invalid change merkle tree + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let burn_amount = 1; + let invalid_change_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .nullifier_queue_pubkey; + let (_, _, _, _, instruction) = create_burn_test_instruction( + &sender, + &mut rpc, + &mut test_indexer, + &input_compressed_accounts, + &invalid_change_account_merkle_tree, + burn_amount, + false, + BurnInstructionMode::Normal, + is_token_22, + ) + .await; + let res = rpc + .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &sender]) + .await; + assert_rpc_error( + res, + 0, + anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into(), + ) + .unwrap(); + } } - // 1. invalid proof - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let burn_amount = 1; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - let (_, _, _, _, instruction) = create_burn_test_instruction( - &sender, +} + +/// Test freeze and thaw: +/// 1. Freeze tokens +/// 2. Thaw tokens +/// 3. Delegate tokens +/// 4. Freeze delegated tokens +/// 5. Thaw delegated tokens +async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { + spawn_prover( + false, + ProverConfig { + run_mode: None, + circuits: vec![ProofType::Inclusion], + }, + ) + .await; + for is_token_22 in [false, true] { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + let delegate = Keypair::new(); + airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, - &input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - false, - BurnInstructionMode::InvalidProof, + &merkle_tree_pubkey, + &payer, + &mint, + vec![mint_amount], + vec![sender.pubkey()], + Some(1_000_000), + is_token_22, ) .await; - let res = rpc - .create_and_send_transaction(&[instruction], &sender.pubkey(), &[&payer, &sender]) - .await; - assert_rpc_error(res, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); - } - // 2. Signer is delegate but token data has no delegate. - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let burn_amount = 1; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - let (_, _, _, _, instruction) = create_burn_test_instruction( - &delegate, - &mut rpc, - &mut test_indexer, - &input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - true, - BurnInstructionMode::Normal, - ) - .await; - let res = rpc - .create_and_send_transaction(&[instruction], &delegate.pubkey(), &[&payer, &delegate]) - .await; - assert_rpc_error(res, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); - } - // 3. Signer is delegate but token data has no delegate. - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let input_compressed_accounts = input_compressed_accounts - .iter() - .filter(|x| x.token_data.delegate.is_some()) - .cloned() - .collect::>(); - let burn_amount = 1; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - let (_, _, _, _, instruction) = create_burn_test_instruction( - &sender, - &mut rpc, - &mut test_indexer, - &input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - true, - BurnInstructionMode::Normal, - ) - .await; - let res = rpc - .create_and_send_transaction(&[instruction], &sender.pubkey(), &[&payer, &sender]) - .await; - assert_rpc_error(res, 0, ErrorCode::DelegateSignerCheckFailed.into()).unwrap(); - } - // 4. invalid authority (use delegate as authority) - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let burn_amount = 1; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - let (_, _, _, _, instruction) = create_burn_test_instruction( - &delegate, - &mut rpc, - &mut test_indexer, - &input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - false, - BurnInstructionMode::Normal, - ) - .await; - let res = rpc - .create_and_send_transaction(&[instruction], &delegate.pubkey(), &[&payer, &delegate]) - .await; - assert_rpc_error(res, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); - } - // 5. invalid mint - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let burn_amount = 1; - let change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - let (_, _, _, _, instruction) = create_burn_test_instruction( - &sender, - &mut rpc, - &mut test_indexer, - &input_compressed_accounts, - &change_account_merkle_tree, - burn_amount, - false, - BurnInstructionMode::InvalidMint, - ) - .await; - let res = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &sender]) - .await; - assert_rpc_error( - res, - 0, - anchor_lang::error::ErrorCode::AccountNotInitialized.into(), - ) - .unwrap(); - } - // 6. invalid change merkle tree - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let burn_amount = 1; - let invalid_change_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .nullifier_queue_pubkey; - let (_, _, _, _, instruction) = create_burn_test_instruction( - &sender, - &mut rpc, - &mut test_indexer, - &input_compressed_accounts, - &invalid_change_account_merkle_tree, - burn_amount, - false, - BurnInstructionMode::Normal, - ) - .await; - let res = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &sender]) - .await; - assert_rpc_error( - res, - 0, - anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into(), - ) - .unwrap(); - } -} - -/// Test freeze and thaw: -/// 1. Freeze tokens -/// 2. Thaw tokens -/// 3. Delegate tokens -/// 4. Freeze delegated tokens -/// 5. Thaw delegated tokens -async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer = rpc.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { - run_mode: None, - circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let delegate = Keypair::new(); - airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut rpc, &payer).await; - mint_tokens_helper_with_lamports( - &mut rpc, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![mint_amount], - vec![sender.pubkey()], - Some(1_000_000), - ) - .await; - // 1. Freeze tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let output_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - - freeze_test( - &payer, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &output_merkle_tree, - None, - ) - .await; - } - // 2. Thaw tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let input_compressed_accounts = input_compressed_accounts - .iter() - .filter(|x| x.token_data.state == AccountState::Frozen) - .cloned() - .collect::>(); - let output_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - thaw_test( - &payer, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &output_merkle_tree, - None, - ) - .await; - } - // 3. Delegate tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - approve_test( - &sender, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - delegated_amount, - None, - &delegate.pubkey(), - &delegated_compressed_account_merkle_tree, - &delegated_compressed_account_merkle_tree, - None, - ) - .await; - } - // 4. Freeze delegated tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let output_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - - freeze_test( - &payer, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &output_merkle_tree, - None, - ) - .await; - } - // 5. Thaw delegated tokens - { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let input_compressed_accounts = input_compressed_accounts - .iter() - .filter(|x| x.token_data.state == AccountState::Frozen) - .cloned() - .collect::>(); - let output_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - - thaw_test( - &payer, - &mut rpc, - &mut test_indexer, - input_compressed_accounts, - &output_merkle_tree, - None, - ) - .await; - } -} - -#[tokio::test] -async fn test_freeze_and_thaw_0() { - test_freeze_and_thaw(0, 0).await -} - -#[tokio::test] -async fn test_freeze_and_thaw_10000() { - test_freeze_and_thaw(10000, 1000).await -} - -/// Failing tests: -/// 1. Invalid authority. -/// 2. Invalid Merkle tree. -/// 3. Invalid proof. -/// 4. Freeze frozen compressed account. -#[tokio::test] -async fn test_failing_freeze() { - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer = rpc.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { - run_mode: None, - circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let delegate = Keypair::new(); - airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut rpc, &payer).await; - let amount = 10000u64; - mint_tokens_helper( - &mut rpc, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![amount; 3], - vec![sender.pubkey(); 3], - ) - .await; - - let input_compressed_accounts = - vec![test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey())[0].clone()]; - let outputs_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - - let input_compressed_account_hashes = input_compressed_accounts - .iter() - .map(|x| x.compressed_account.hash().unwrap()) - .collect::>(); - let input_merkle_tree_pubkeys = input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey) - .collect::>(); - let proof_rpc_result = test_indexer - .create_proof_for_compressed_accounts( - Some(&input_compressed_account_hashes), - Some(&input_merkle_tree_pubkeys), - None, - None, - &mut rpc, - ) - .await; - let context_payer = rpc.get_payer().insecure_clone(); - - // 1. Invalid authority. - { - let invalid_authority = Keypair::new(); + // 1. Freeze tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let output_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: invalid_authority.pubkey(), - input_merkle_contexts: input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts - .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts - .iter() - .map(|x| &x.compressed_account.compressed_account) - .cloned() - .collect::>(), - outputs_merkle_tree, - root_indices: proof_rpc_result.root_indices.clone(), - proof: proof_rpc_result.proof.clone(), - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[&context_payer, &invalid_authority], + freeze_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &output_merkle_tree, + None, ) .await; - assert_rpc_error(result, 0, ErrorCode::InvalidFreezeAuthority.into()).unwrap(); - } - // 2. Invalid Merkle tree. - { - let invalid_merkle_tree = Keypair::new(); - - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: payer.pubkey(), - input_merkle_contexts: input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts - .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts + } + // 2. Thaw tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = input_compressed_accounts .iter() - .map(|x| &x.compressed_account.compressed_account) + .filter(|x| x.token_data.state == AccountState::Frozen) .cloned() - .collect::>(), - outputs_merkle_tree: invalid_merkle_tree.pubkey(), - root_indices: proof_rpc_result.root_indices.clone(), - proof: proof_rpc_result.proof.clone(), - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&context_payer, &payer]) + .collect::>(); + let output_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + thaw_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &output_merkle_tree, + None, + ) .await; - // Anchor panics when trying to read the MT account. Unfortunately - // there is no specific error code to assert. - assert!(matches!( - result, - Err(RpcError::TransactionError( - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) - )) - )); - } - // 3. Invalid proof. - { - let invalid_proof = CompressedProof { - a: [0; 32], - b: [0; 64], - c: [0; 32], - }; + } + // 3. Delegate tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + approve_test( + &sender, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + delegated_amount, + None, + &delegate.pubkey(), + &delegated_compressed_account_merkle_tree, + &delegated_compressed_account_merkle_tree, + None, + ) + .await; + } + // 4. Freeze delegated tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let output_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: payer.pubkey(), - input_merkle_contexts: input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts - .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts + freeze_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &output_merkle_tree, + None, + ) + .await; + } + // 5. Thaw delegated tokens + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = input_compressed_accounts .iter() - .map(|x| &x.compressed_account.compressed_account) + .filter(|x| x.token_data.state == AccountState::Frozen) .cloned() - .collect::>(), - outputs_merkle_tree, - root_indices: proof_rpc_result.root_indices.clone(), - proof: invalid_proof, - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&context_payer, &payer]) + .collect::>(); + let output_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + + thaw_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &output_merkle_tree, + None, + ) .await; - assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } } - // 4. Freeze frozen compressed account - { - freeze_test( - &payer, +} + +#[tokio::test] +async fn test_freeze_and_thaw_0() { + test_freeze_and_thaw(0, 0).await +} + +#[tokio::test] +async fn test_freeze_and_thaw_10000() { + test_freeze_and_thaw(10000, 1000).await +} + +/// Failing tests: +/// 1. Invalid authority. +/// 2. Invalid Merkle tree. +/// 3. Invalid proof. +/// 4. Freeze frozen compressed account. +#[tokio::test] +async fn test_failing_freeze() { + spawn_prover( + false, + ProverConfig { + run_mode: None, + circuits: vec![ProofType::Inclusion], + }, + ) + .await; + for is_token_22 in [false, true] { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + let delegate = Keypair::new(); + airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; + let amount = 10000u64; + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, - input_compressed_accounts, - &outputs_merkle_tree, + &merkle_tree_pubkey, + &payer, + &mint, + vec![amount; 3], + vec![sender.pubkey(); 3], None, + is_token_22, ) .await; - let input_compressed_accounts = vec![test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) - .iter() - .filter(|x| x.token_data.state == AccountState::Frozen) - .cloned() - .collect::>()[0] - .clone()]; + + let input_compressed_accounts = + vec![test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey())[0].clone()]; let outputs_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -2654,31 +2683,194 @@ async fn test_failing_freeze() { &mut rpc, ) .await; - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: payer.pubkey(), - input_merkle_contexts: input_compressed_accounts + let context_payer = rpc.get_payer().insecure_clone(); + + // 1. Invalid authority. + { + let invalid_authority = Keypair::new(); + + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: invalid_authority.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree, + root_indices: proof_rpc_result.root_indices.clone(), + proof: proof_rpc_result.proof.clone(), + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &invalid_authority], + ) + .await; + assert_rpc_error(result, 0, ErrorCode::InvalidFreezeAuthority.into()).unwrap(); + } + // 2. Invalid Merkle tree. + { + let invalid_merkle_tree = Keypair::new(); + + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: payer.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree: invalid_merkle_tree.pubkey(), + root_indices: proof_rpc_result.root_indices.clone(), + proof: proof_rpc_result.proof.clone(), + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &payer], + ) + .await; + // Anchor panics when trying to read the MT account. Unfortunately + // there is no specific error code to assert. + assert!(matches!( + result, + Err(RpcError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::ProgramFailedToComplete + ) + )) + )); + } + // 3. Invalid proof. + { + let invalid_proof = CompressedProof { + a: [0; 32], + b: [0; 64], + c: [0; 32], + }; + + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: payer.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree, + root_indices: proof_rpc_result.root_indices.clone(), + proof: invalid_proof, + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &payer], + ) + .await; + assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } + // 4. Freeze frozen compressed account + { + freeze_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &outputs_merkle_tree, + None, + ) + .await; + let input_compressed_accounts = vec![test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey()) .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts + .filter(|x| x.token_data.state == AccountState::Frozen) + .cloned() + .collect::>()[0] + .clone()]; + let outputs_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + + let input_compressed_account_hashes = input_compressed_accounts .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts + .map(|x| x.compressed_account.hash().unwrap()) + .collect::>(); + let input_merkle_tree_pubkeys = input_compressed_accounts .iter() - .map(|x| &x.compressed_account.compressed_account) - .cloned() - .collect::>(), - outputs_merkle_tree, - root_indices: proof_rpc_result.root_indices.clone(), - proof: proof_rpc_result.proof.clone(), - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&context_payer, &payer]) - .await; - assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey) + .collect::>(); + let proof_rpc_result = test_indexer + .create_proof_for_compressed_accounts( + Some(&input_compressed_account_hashes), + Some(&input_merkle_tree_pubkeys), + None, + None, + &mut rpc, + ) + .await; + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: payer.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree, + root_indices: proof_rpc_result.root_indices.clone(), + proof: proof_rpc_result.proof.clone(), + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &payer], + ) + .await; + assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } } } @@ -2689,202 +2881,73 @@ async fn test_failing_freeze() { /// 4. thaw compressed account which is not frozen #[tokio::test] async fn test_failing_thaw() { - let (mut rpc, env) = setup_test_programs_with_accounts(None).await; - let payer = rpc.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { + spawn_prover( + false, + ProverConfig { run_mode: None, circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let delegate = Keypair::new(); - airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut rpc, &payer).await; - let amount = 10000u64; - mint_tokens_helper( - &mut rpc, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![amount; 2], - vec![sender.pubkey(); 2], + }, ) .await; - - // Freeze tokens - { - let input_compressed_accounts = - vec![test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey())[0].clone()]; - let output_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - - freeze_test( - &payer, + for is_token_22 in [false, true] { + let (mut rpc, env) = setup_test_programs_with_accounts(None).await; + let payer = rpc.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut rpc, &sender.pubkey(), 1_000_000_000) + .await + .unwrap(); + let delegate = Keypair::new(); + airdrop_lamports(&mut rpc, &delegate.pubkey(), 1_000_000_000) + .await + .unwrap(); + let mint = if is_token_22 { + create_mint_22_helper(&mut rpc, &payer).await + } else { + create_mint_helper(&mut rpc, &payer).await + }; + let amount = 10000u64; + mint_tokens_22_helper_with_lamports( &mut rpc, &mut test_indexer, - input_compressed_accounts, - &output_merkle_tree, - None, - ) - .await; - } - - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let input_compressed_accounts = input_compressed_accounts - .iter() - .filter(|x| x.token_data.state == AccountState::Frozen) - .cloned() - .collect::>(); - let outputs_merkle_tree = input_compressed_accounts[0] - .compressed_account - .merkle_context - .merkle_tree_pubkey; - - let input_compressed_account_hashes = input_compressed_accounts - .iter() - .map(|x| x.compressed_account.hash().unwrap()) - .collect::>(); - let input_merkle_tree_pubkeys = input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey) - .collect::>(); - let proof_rpc_result = test_indexer - .create_proof_for_compressed_accounts( - Some(&input_compressed_account_hashes), - Some(&input_merkle_tree_pubkeys), - None, + &merkle_tree_pubkey, + &payer, + &mint, + vec![amount; 2], + vec![sender.pubkey(); 2], None, - &mut rpc, + is_token_22, ) .await; - let context_payer = rpc.get_payer().insecure_clone(); - - // 1. Invalid authority. - { - let invalid_authority = Keypair::new(); - - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: invalid_authority.pubkey(), - input_merkle_contexts: input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts - .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts - .iter() - .map(|x| &x.compressed_account.compressed_account) - .cloned() - .collect::>(), - outputs_merkle_tree, - root_indices: proof_rpc_result.root_indices.clone(), - proof: proof_rpc_result.proof.clone(), - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction( - &[instruction], - &payer.pubkey(), - &[&context_payer, &invalid_authority], - ) - .await; - assert_rpc_error(result, 0, ErrorCode::InvalidFreezeAuthority.into()).unwrap(); - } - // 2. Invalid Merkle tree. - { - let invalid_merkle_tree = Keypair::new(); - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: payer.pubkey(), - input_merkle_contexts: input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts - .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts - .iter() - .map(|x| &x.compressed_account.compressed_account) - .cloned() - .collect::>(), - outputs_merkle_tree: invalid_merkle_tree.pubkey(), - root_indices: proof_rpc_result.root_indices.clone(), - proof: proof_rpc_result.proof.clone(), - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&context_payer, &payer]) - .await; - // Anchor panics when trying to read the MT account. Unfortunately - // there is no specific error code to assert. - assert!(matches!( - result, - Err(RpcError::TransactionError( - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) - )) - )); - } - // 3. Invalid proof. - { - let invalid_proof = CompressedProof { - a: [0; 32], - b: [0; 64], - c: [0; 32], - }; + // Freeze tokens + { + let input_compressed_accounts = vec![test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey())[0] + .clone()]; + let output_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: payer.pubkey(), - input_merkle_contexts: input_compressed_accounts - .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts - .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts - .iter() - .map(|x| &x.compressed_account.compressed_account) - .cloned() - .collect::>(), - outputs_merkle_tree, - root_indices: proof_rpc_result.root_indices.clone(), - proof: invalid_proof, - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&context_payer, &payer]) + freeze_test( + &payer, + &mut rpc, + &mut test_indexer, + input_compressed_accounts, + &output_merkle_tree, + None, + ) .await; - assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); - } - // 4. thaw compressed account which is not frozen - { + } + let input_compressed_accounts = test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); let input_compressed_accounts = input_compressed_accounts .iter() - .filter(|x| x.token_data.state == AccountState::Initialized) + .filter(|x| x.token_data.state == AccountState::Frozen) .cloned() .collect::>(); let outputs_merkle_tree = input_compressed_accounts[0] @@ -2909,31 +2972,185 @@ async fn test_failing_thaw() { &mut rpc, ) .await; - let inputs = CreateInstructionInputs { - fee_payer: rpc.get_payer().pubkey(), - authority: payer.pubkey(), - input_merkle_contexts: input_compressed_accounts + let context_payer = rpc.get_payer().insecure_clone(); + + // 1. Invalid authority. + { + let invalid_authority = Keypair::new(); + + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: invalid_authority.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree, + root_indices: proof_rpc_result.root_indices.clone(), + proof: proof_rpc_result.proof.clone(), + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &invalid_authority], + ) + .await; + assert_rpc_error(result, 0, ErrorCode::InvalidFreezeAuthority.into()).unwrap(); + } + // 2. Invalid Merkle tree. + { + let invalid_merkle_tree = Keypair::new(); + + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: payer.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree: invalid_merkle_tree.pubkey(), + root_indices: proof_rpc_result.root_indices.clone(), + proof: proof_rpc_result.proof.clone(), + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &payer], + ) + .await; + // Anchor panics when trying to read the MT account. Unfortunately + // there is no specific error code to assert. + assert!(matches!( + result, + Err(RpcError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::ProgramFailedToComplete + ) + )) + )); + } + // 3. Invalid proof. + { + let invalid_proof = CompressedProof { + a: [0; 32], + b: [0; 64], + c: [0; 32], + }; + + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: payer.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree, + root_indices: proof_rpc_result.root_indices.clone(), + proof: invalid_proof, + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &payer], + ) + .await; + assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } + // 4. thaw compressed account which is not frozen + { + let input_compressed_accounts = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = input_compressed_accounts .iter() - .map(|x| x.compressed_account.merkle_context) - .collect(), - input_token_data: input_compressed_accounts + .filter(|x| x.token_data.state == AccountState::Initialized) + .cloned() + .collect::>(); + let outputs_merkle_tree = input_compressed_accounts[0] + .compressed_account + .merkle_context + .merkle_tree_pubkey; + + let input_compressed_account_hashes = input_compressed_accounts .iter() - .map(|x| x.token_data.clone()) - .collect(), - input_compressed_accounts: input_compressed_accounts + .map(|x| x.compressed_account.hash().unwrap()) + .collect::>(); + let input_merkle_tree_pubkeys = input_compressed_accounts .iter() - .map(|x| &x.compressed_account.compressed_account) - .cloned() - .collect::>(), - outputs_merkle_tree, - root_indices: proof_rpc_result.root_indices.clone(), - proof: proof_rpc_result.proof.clone(), - }; - let instruction = create_instruction::(inputs).unwrap(); - let result = rpc - .create_and_send_transaction(&[instruction], &payer.pubkey(), &[&context_payer, &payer]) - .await; - assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + .map(|x| x.compressed_account.merkle_context.merkle_tree_pubkey) + .collect::>(); + let proof_rpc_result = test_indexer + .create_proof_for_compressed_accounts( + Some(&input_compressed_account_hashes), + Some(&input_merkle_tree_pubkeys), + None, + None, + &mut rpc, + ) + .await; + let inputs = CreateInstructionInputs { + fee_payer: rpc.get_payer().pubkey(), + authority: payer.pubkey(), + input_merkle_contexts: input_compressed_accounts + .iter() + .map(|x| x.compressed_account.merkle_context) + .collect(), + input_token_data: input_compressed_accounts + .iter() + .map(|x| x.token_data.clone()) + .collect(), + input_compressed_accounts: input_compressed_accounts + .iter() + .map(|x| &x.compressed_account.compressed_account) + .cloned() + .collect::>(), + outputs_merkle_tree, + root_indices: proof_rpc_result.root_indices.clone(), + proof: proof_rpc_result.proof.clone(), + }; + let instruction = create_instruction::(inputs).unwrap(); + let result = rpc + .create_and_send_transaction( + &[instruction], + &payer.pubkey(), + &[&context_payer, &payer], + ) + .await; + assert_rpc_error(result, 0, VerifierError::ProofVerificationFailed.into()).unwrap(); + } } } @@ -2949,264 +3166,303 @@ async fn test_failing_thaw() { /// 9. Invalid compression amount 0 #[tokio::test] async fn test_failing_decompression() { - let (mut context, env) = setup_test_programs_with_accounts(None).await; - let payer = context.get_payer().insecure_clone(); - let merkle_tree_pubkey = env.merkle_tree_pubkey; - let mut test_indexer = TestIndexer::::init_from_env( - &payer, - &env, - Some(ProverConfig { + spawn_prover( + true, + ProverConfig { run_mode: None, circuits: vec![ProofType::Inclusion], - }), - ) - .await; - let sender = Keypair::new(); - airdrop_lamports(&mut context, &sender.pubkey(), 1_000_000_000) - .await - .unwrap(); - let mint = create_mint_helper(&mut context, &payer).await; - let amount = 10000u64; - mint_tokens_helper( - &mut context, - &mut test_indexer, - &merkle_tree_pubkey, - &payer, - &mint, - vec![amount], - vec![sender.pubkey()], + }, ) .await; - let token_account_keypair = Keypair::new(); - create_token_account(&mut context, &mint, &token_account_keypair, &sender) - .await - .unwrap(); - let input_compressed_account = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); - let decompress_amount = amount - 1000; - // Test 1: invalid decompress account - { - let invalid_token_account = mint; - failing_compress_decompress( - &sender, - &mut context, - &mut test_indexer, - input_compressed_account.clone(), - decompress_amount, // needs to be consistent with compression amount - &merkle_tree_pubkey, - decompress_amount, - false, - &invalid_token_account, - Some(get_token_pool_pda(&mint)), - &mint, - 0, //ProgramError::InvalidAccountData.into(), error code 17179869184 does not fit u32 - ) - .await - .unwrap_err(); - } - // Test 2: invalid token pool pda (compress and decompress) - { - let invalid_token_account_keypair = Keypair::new(); - create_token_account(&mut context, &mint, &invalid_token_account_keypair, &payer) - .await - .unwrap(); - failing_compress_decompress( - &sender, - &mut context, - &mut test_indexer, - input_compressed_account.clone(), - decompress_amount, // needs to be consistent with compression amount - &merkle_tree_pubkey, - decompress_amount, - false, - &token_account_keypair.pubkey(), - Some(invalid_token_account_keypair.pubkey()), - &mint, - ErrorCode::InvalidTokenPoolPda.into(), - ) - .await - .unwrap(); - - let invalid_token_account_keypair = Keypair::new(); - create_token_account(&mut context, &mint, &invalid_token_account_keypair, &payer) + for is_token_22 in vec![false, true] { + let (mut context, env) = setup_test_programs_with_accounts(None).await; + let payer = context.get_payer().insecure_clone(); + let merkle_tree_pubkey = env.merkle_tree_pubkey; + let mut test_indexer = + TestIndexer::::init_from_env(&payer, &env, None).await; + let sender = Keypair::new(); + airdrop_lamports(&mut context, &sender.pubkey(), 1_000_000_000) .await .unwrap(); - failing_compress_decompress( - &sender, - &mut context, - &mut test_indexer, - input_compressed_account.clone(), - 0, // needs to be consistent with compression amount - &merkle_tree_pubkey, - 0, - true, - &token_account_keypair.pubkey(), - Some(invalid_token_account_keypair.pubkey()), - &mint, - ErrorCode::InvalidTokenPoolPda.into(), - ) - .await - .unwrap(); - } - // Test 3: invalid compression amount -1 - { - failing_compress_decompress( - &sender, - &mut context, - &mut test_indexer, - input_compressed_account.clone(), - decompress_amount, // needs to be consistent with compression amount - &merkle_tree_pubkey, - decompress_amount - 1, - false, - &token_account_keypair.pubkey(), - Some(get_token_pool_pda(&mint)), - &mint, - ErrorCode::SumCheckFailed.into(), - ) - .await - .unwrap(); - } - // Test 4: invalid compression amount + 1 - { - failing_compress_decompress( - &sender, + let mint = if is_token_22 { + create_mint_22_helper(&mut context, &payer).await + } else { + create_mint_helper(&mut context, &payer).await + }; + let amount = 10000u64; + mint_tokens_22_helper_with_lamports( &mut context, &mut test_indexer, - input_compressed_account.clone(), - decompress_amount, // needs to be consistent with compression amount &merkle_tree_pubkey, - decompress_amount + 1, - false, - &token_account_keypair.pubkey(), - Some(get_token_pool_pda(&mint)), + &payer, &mint, - ErrorCode::ComputeOutputSumFailed.into(), + vec![amount], + vec![sender.pubkey()], + None, + is_token_22, ) - .await - .unwrap(); - } - // Test 5: invalid compression amount 0 - { - failing_compress_decompress( - &sender, + .await; + let token_account_keypair = Keypair::new(); + create_token_2022_account( &mut context, - &mut test_indexer, - input_compressed_account.clone(), - decompress_amount, // needs to be consistent with compression amount - &merkle_tree_pubkey, - 0, - false, - &token_account_keypair.pubkey(), - Some(get_token_pool_pda(&mint)), &mint, - ErrorCode::SumCheckFailed.into(), - ) - .await - .unwrap(); - } - // Test 6: invalid token recipient - { - failing_compress_decompress( + &token_account_keypair, &sender, - &mut context, - &mut test_indexer, - input_compressed_account.clone(), - decompress_amount, // needs to be consistent with compression amount - &merkle_tree_pubkey, - decompress_amount, - false, - &get_token_pool_pda(&mint), - Some(get_token_pool_pda(&mint)), - &mint, - ErrorCode::IsTokenPoolPda.into(), + is_token_22, ) .await .unwrap(); - } + let input_compressed_account = + test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let decompress_amount = amount - 1000; + // Test 1: invalid decompress account + { + let invalid_token_account = mint; + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + decompress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + decompress_amount, + false, + &invalid_token_account, + Some(get_token_pool_pda(&mint)), + &mint, + 0, //ProgramError::InvalidAccountData.into(), error code 17179869184 does not fit u32 + is_token_22, + ) + .await + .unwrap_err(); + } + // Test 2: invalid token pool pda (compress and decompress) + { + let invalid_token_account_keypair = Keypair::new(); + create_token_2022_account( + &mut context, + &mint, + &invalid_token_account_keypair, + &payer, + is_token_22, + ) + .await + .unwrap(); + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + decompress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + decompress_amount, + false, + &token_account_keypair.pubkey(), + Some(invalid_token_account_keypair.pubkey()), + &mint, + ErrorCode::InvalidTokenPoolPda.into(), + is_token_22, + ) + .await + .unwrap(); - // functional so that we have tokens to compress - decompress_test( - &sender, - &mut context, - &mut test_indexer, - input_compressed_account, - amount, - &merkle_tree_pubkey, - &token_account_keypair.pubkey(), - None, - ) - .await; - let compress_amount = decompress_amount - 100; - // Test 7: invalid compression amount -1 - { - failing_compress_decompress( + let invalid_token_account_keypair = Keypair::new(); + create_token_2022_account( + &mut context, + &mint, + &invalid_token_account_keypair, + &payer, + is_token_22, + ) + .await + .unwrap(); + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + 0, // needs to be consistent with compression amount + &merkle_tree_pubkey, + 0, + true, + &token_account_keypair.pubkey(), + Some(invalid_token_account_keypair.pubkey()), + &mint, + ErrorCode::InvalidTokenPoolPda.into(), + is_token_22, + ) + .await + .unwrap(); + } + // Test 3: invalid compression amount -1 + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + decompress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + decompress_amount - 1, + false, + &token_account_keypair.pubkey(), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::SumCheckFailed.into(), + is_token_22, + ) + .await + .unwrap(); + } + // Test 4: invalid compression amount + 1 + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + decompress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + decompress_amount + 1, + false, + &token_account_keypair.pubkey(), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::ComputeOutputSumFailed.into(), + is_token_22, + ) + .await + .unwrap(); + } + // Test 5: invalid compression amount 0 + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + decompress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + 0, + false, + &token_account_keypair.pubkey(), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::SumCheckFailed.into(), + is_token_22, + ) + .await + .unwrap(); + } + // Test 6: invalid token recipient + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + input_compressed_account.clone(), + decompress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + decompress_amount, + false, + &get_token_pool_pda(&mint), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::IsTokenPoolPda.into(), + is_token_22, + ) + .await + .unwrap(); + } + + // functional so that we have tokens to compress + decompress_test( &sender, &mut context, &mut test_indexer, - Vec::new(), - compress_amount, // needs to be consistent with compression amount + input_compressed_account, + amount, &merkle_tree_pubkey, - compress_amount - 1, - true, &token_account_keypair.pubkey(), - Some(get_token_pool_pda(&mint)), - &mint, - ErrorCode::ComputeOutputSumFailed.into(), + None, + is_token_22, ) - .await - .unwrap(); - } - // Test 8: invalid compression amount +1 - { - failing_compress_decompress( + .await; + let compress_amount = decompress_amount - 100; + // Test 7: invalid compression amount -1 + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + Vec::new(), + compress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + compress_amount - 1, + true, + &token_account_keypair.pubkey(), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::ComputeOutputSumFailed.into(), + is_token_22, + ) + .await + .unwrap(); + } + // Test 8: invalid compression amount +1 + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + Vec::new(), + compress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + compress_amount + 1, + true, + &token_account_keypair.pubkey(), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::SumCheckFailed.into(), + is_token_22, + ) + .await + .unwrap(); + } + // Test 9: invalid compression amount 0 + { + failing_compress_decompress( + &sender, + &mut context, + &mut test_indexer, + Vec::new(), + compress_amount, // needs to be consistent with compression amount + &merkle_tree_pubkey, + 0, + true, + &token_account_keypair.pubkey(), + Some(get_token_pool_pda(&mint)), + &mint, + ErrorCode::ComputeOutputSumFailed.into(), + is_token_22, + ) + .await + .unwrap(); + } + // functional + compress_test( &sender, &mut context, &mut test_indexer, - Vec::new(), - compress_amount, // needs to be consistent with compression amount - &merkle_tree_pubkey, - compress_amount + 1, - true, - &token_account_keypair.pubkey(), - Some(get_token_pool_pda(&mint)), + amount, &mint, - ErrorCode::SumCheckFailed.into(), - ) - .await - .unwrap(); - } - // Test 9: invalid compression amount 0 - { - failing_compress_decompress( - &sender, - &mut context, - &mut test_indexer, - Vec::new(), - compress_amount, // needs to be consistent with compression amount &merkle_tree_pubkey, - 0, - true, &token_account_keypair.pubkey(), - Some(get_token_pool_pda(&mint)), - &mint, - ErrorCode::ComputeOutputSumFailed.into(), + None, + is_token_22, ) - .await - .unwrap(); + .await; } - // functional - compress_test( - &sender, - &mut context, - &mut test_indexer, - amount, - &mint, - &merkle_tree_pubkey, - &token_account_keypair.pubkey(), - None, - ) - .await; kill_prover(); } @@ -3224,6 +3480,7 @@ pub async fn failing_compress_decompress( token_pool_pda: Option, mint: &Pubkey, error_code: u32, + is_token_22: bool, ) -> Result<(), RpcError> { let max_amount: u64 = input_compressed_accounts .iter() @@ -3296,12 +3553,23 @@ pub async fn failing_compress_decompress( true, None, None, + is_token_22, ) .unwrap(); let instructions = if !is_compress { vec![instruction] } else { - vec![ + let approve_instruction = if is_token_22 { + spl_token_2022::instruction::approve( + &spl_token_2022::ID, + compress_or_decompress_token_account, + &get_cpi_authority_pda().0, + &payer.pubkey(), + &[&payer.pubkey()], + amount, + ) + .unwrap() + } else { spl_token::instruction::approve( &anchor_spl::token::ID, compress_or_decompress_token_account, @@ -3310,9 +3578,9 @@ pub async fn failing_compress_decompress( &[&payer.pubkey()], amount, ) - .unwrap(), - instruction, - ] + .unwrap() + }; + vec![approve_instruction, instruction] }; let context_payer = rpc.get_payer().insecure_clone(); @@ -3749,6 +4017,7 @@ async fn perform_transfer_failing_test( true, None, None, + false, ) .unwrap(); diff --git a/test-programs/system-cpi-test/tests/test_program_owned_trees.rs b/test-programs/system-cpi-test/tests/test_program_owned_trees.rs index af518b1159..3b0d87daab 100644 --- a/test-programs/system-cpi-test/tests/test_program_owned_trees.rs +++ b/test-programs/system-cpi-test/tests/test_program_owned_trees.rs @@ -84,6 +84,7 @@ async fn test_program_owned_merkle_tree() { vec![amount; 1], vec![recipient_keypair.pubkey(); 1], None, + false, ); let pre_merkle_tree = get_concurrent_merkle_tree::< StateMerkleTreeAccount, @@ -146,6 +147,7 @@ async fn test_program_owned_merkle_tree() { vec![amount + 1; 1], vec![recipient_keypair.pubkey(); 1], None, + false, ); let latest_blockhash = rpc.get_latest_blockhash().await.unwrap(); diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 05737bb4d6..94e50c581b 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -46,6 +46,7 @@ log = "0.4" serde = { version = "1.0.197", features = ["derive"] } async-trait = "0.1.82" light-client = { workspace = true } +spl-token-2022 = "3.0.0" [dev-dependencies] rand = "0.8" diff --git a/test-utils/src/e2e_test_env.rs b/test-utils/src/e2e_test_env.rs index 83c7cd9a46..91fdb8fadc 100644 --- a/test-utils/src/e2e_test_env.rs +++ b/test-utils/src/e2e_test_env.rs @@ -1602,6 +1602,7 @@ where burn_amount, false, transaction_paramets, + false, ) .await; self.stats.spl_burned += 1; @@ -1746,6 +1747,7 @@ where &output_merkle_tree_account[0], &token_account, transaction_paramets, + false, ) .await; self.stats.spl_compress += 1; @@ -1822,6 +1824,7 @@ where &output_merkle_tree_account[0], &token_account, transaction_paramets, + false, ) .await; self.stats.spl_decompress += 1; diff --git a/test-utils/src/spl.rs b/test-utils/src/spl.rs index c342f6bba7..557deb4326 100644 --- a/test-utils/src/spl.rs +++ b/test-utils/src/spl.rs @@ -1,6 +1,7 @@ use anchor_spl::token::{Mint, TokenAccount}; use forester_utils::create_account_instruction; use forester_utils::indexer::{Indexer, TokenDataWithContext}; +use light_compressed_token::mint_sdk::create_create_token_pool_2022_instruction; use light_compressed_token::process_compress_spl_token_account::sdk::create_compress_spl_token_account_instruction; use light_compressed_token::{ burn::sdk::{create_burn_instruction, CreateBurnInstructionInputs}, @@ -66,16 +67,29 @@ pub async fn mint_spl_tokens( token_owner: &Pubkey, mint_authority: &Keypair, amount: u64, + is_token_22: bool, ) -> Result { - let mint_to_instruction = spl_token::instruction::mint_to( - &spl_token::ID, - mint, - token_account, - token_owner, - &[&mint_authority.pubkey()], - amount, - ) - .unwrap(); + let mint_to_instruction = if is_token_22 { + spl_token_2022::instruction::mint_to( + &spl_token_2022::ID, + mint, + token_account, + token_owner, + &[&mint_authority.pubkey()], + amount, + ) + .unwrap() + } else { + spl_token::instruction::mint_to( + &spl_token::ID, + mint, + token_account, + token_owner, + &[&mint_authority.pubkey()], + amount, + ) + .unwrap() + }; rpc.create_and_send_transaction( &[mint_to_instruction], &mint_authority.pubkey(), @@ -94,6 +108,31 @@ pub async fn mint_tokens_helper_with_lamports>( amounts: Vec, recipients: Vec, lamports: Option, +) { + mint_tokens_22_helper_with_lamports( + rpc, + test_indexer, + merkle_tree_pubkey, + mint_authority, + mint, + amounts, + recipients, + lamports, + false, + ) + .await; +} +#[allow(clippy::too_many_arguments)] +pub async fn mint_tokens_22_helper_with_lamports>( + rpc: &mut R, + test_indexer: &mut I, + merkle_tree_pubkey: &Pubkey, + mint_authority: &Keypair, + mint: &Pubkey, + amounts: Vec, + recipients: Vec, + lamports: Option, + token_22: bool, ) { let payer_pubkey = mint_authority.pubkey(); let instruction = create_mint_to_instruction( @@ -104,6 +143,7 @@ pub async fn mint_tokens_helper_with_lamports>( amounts.clone(), recipients.clone(), lamports, + token_22, ); let output_merkle_tree_accounts = @@ -194,24 +234,47 @@ pub async fn create_mint_helper(rpc: &mut R, payer: &Keypair) mint.pubkey() } +pub async fn create_mint_22_helper(rpc: &mut R, payer: &Keypair) -> Pubkey { + let payer_pubkey = payer.pubkey(); + let rent = rpc + .get_minimum_balance_for_rent_exemption(Mint::LEN) + .await + .unwrap(); + let mint = Keypair::new(); + + let (instructions, pool) = + create_initialize_mint_22_instructions(&payer_pubkey, &payer_pubkey, rent, 2, &mint, true); + + rpc.create_and_send_transaction(&instructions, &payer_pubkey, &[payer, &mint]) + .await + .unwrap(); + assert_create_mint(rpc, &payer_pubkey, &mint.pubkey(), &pool).await; + mint.pubkey() +} + pub async fn mint_wrapped_sol( rpc: &mut R, payer: &Keypair, token_account: &Pubkey, amount: u64, + is_token_22: bool, ) -> Result { let transfer_ix = anchor_lang::solana_program::system_instruction::transfer( &payer.pubkey(), token_account, amount, ); - let sync_native_ix = spl_token::instruction::sync_native(&spl_token::ID, token_account) - .map_err(|e| RpcError::CustomError(format!("{:?}", e)))?; + let sync_native_ix = if is_token_22 { + spl_token_2022::instruction::sync_native(&spl_token_2022::ID, token_account) + .map_err(|e| RpcError::CustomError(format!("{:?}", e)))? + } else { + spl_token::instruction::sync_native(&spl_token::ID, token_account) + .map_err(|e| RpcError::CustomError(format!("{:?}", e)))? + }; rpc.create_and_send_transaction(&[transfer_ix, sync_native_ix], &payer.pubkey(), &[payer]) .await } - pub fn create_initialize_mint_instructions( payer: &Pubkey, authority: &Pubkey, @@ -219,22 +282,53 @@ pub fn create_initialize_mint_instructions( decimals: u8, mint_keypair: &Keypair, ) -> ([Instruction; 4], Pubkey) { + create_initialize_mint_22_instructions(payer, authority, rent, decimals, mint_keypair, false) +} + +pub fn create_initialize_mint_22_instructions( + payer: &Pubkey, + authority: &Pubkey, + rent: u64, + decimals: u8, + mint_keypair: &Keypair, + token_22: bool, +) -> ([Instruction; 4], Pubkey) { + let program_id = if token_22 { + anchor_spl::token_2022::ID + } else { + spl_token::ID + }; let account_create_ix = - create_account_instruction(payer, Mint::LEN, rent, &spl_token::ID, Some(mint_keypair)); + create_account_instruction(payer, Mint::LEN, rent, &program_id, Some(mint_keypair)); let mint_pubkey = mint_keypair.pubkey(); - let create_mint_instruction = initialize_mint( - &spl_token::ID, - &mint_keypair.pubkey(), - authority, - Some(authority), - decimals, - ) - .unwrap(); + let create_mint_instruction = if token_22 { + spl_token_2022::instruction::initialize_mint( + &program_id, + &mint_keypair.pubkey(), + authority, + Some(authority), + decimals, + ) + .unwrap() + } else { + initialize_mint( + &program_id, + &mint_keypair.pubkey(), + authority, + Some(authority), + decimals, + ) + .unwrap() + }; let transfer_ix = anchor_lang::solana_program::system_instruction::transfer(payer, &mint_pubkey, rent); - let instruction = create_create_token_pool_instruction(payer, &mint_pubkey); + let instruction = if token_22 { + create_create_token_pool_2022_instruction(payer, &mint_pubkey) + } else { + create_create_token_pool_instruction(payer, &mint_pubkey) + }; let pool_pubkey = get_token_pool_pda(&mint_pubkey); ( [ @@ -255,24 +349,53 @@ pub async fn create_token_account( account_keypair: &Keypair, owner: &Keypair, ) -> Result<(), BanksClientError> { + create_token_2022_account(rpc, mint, account_keypair, owner, false).await +} +pub async fn create_token_2022_account( + rpc: &mut R, + mint: &Pubkey, + account_keypair: &Keypair, + owner: &Keypair, + token_22: bool, +) -> Result<(), BanksClientError> { + let account_len = if token_22 { + spl_token_2022::state::Account::LEN + } else { + spl_token::state::Account::LEN + }; let rent = rpc - .get_minimum_balance_for_rent_exemption(TokenAccount::LEN) + .get_minimum_balance_for_rent_exemption(account_len) .await .unwrap(); + let program_id = if token_22 { + spl_token_2022::ID + } else { + spl_token::ID + }; let account_create_ix = create_account_instruction( &owner.pubkey(), TokenAccount::LEN, rent, - &spl_token::ID, + &program_id, Some(account_keypair), ); - let instruction = spl_token::instruction::initialize_account( - &spl_token::ID, - &account_keypair.pubkey(), - mint, - &owner.pubkey(), - ) - .unwrap(); + let instruction = if token_22 { + spl_token_2022::instruction::initialize_account( + &program_id, + &account_keypair.pubkey(), + mint, + &owner.pubkey(), + ) + .unwrap() + } else { + spl_token::instruction::initialize_account( + &program_id, + &account_keypair.pubkey(), + mint, + &owner.pubkey(), + ) + .unwrap() + }; rpc.create_and_send_transaction( &[account_create_ix, instruction], &owner.pubkey(), @@ -285,6 +408,41 @@ pub async fn create_token_account( #[allow(clippy::too_many_arguments)] pub async fn compressed_transfer_test>( + payer: &Keypair, + rpc: &mut R, + test_indexer: &mut I, + mint: &Pubkey, + from: &Keypair, + recipients: &[Pubkey], + amounts: &[u64], + lamports: Option>>, + input_compressed_accounts: &[TokenDataWithContext], + output_merkle_tree_pubkeys: &[Pubkey], + delegate_change_account_index: Option, + delegate_is_signer: bool, + transaction_params: Option, +) { + compressed_transfer_22_test( + payer, + rpc, + test_indexer, + mint, + from, + recipients, + amounts, + lamports, + input_compressed_accounts, + output_merkle_tree_pubkeys, + delegate_change_account_index, + delegate_is_signer, + transaction_params, + false, + ) + .await; +} + +#[allow(clippy::too_many_arguments)] +pub async fn compressed_transfer_22_test>( payer: &Keypair, rpc: &mut R, test_indexer: &mut I, @@ -298,6 +456,7 @@ pub async fn compressed_transfer_test>( delegate_change_account_index: Option, delegate_is_signer: bool, transaction_params: Option, + token_22: bool, ) { if recipients.len() != amounts.len() && amounts.len() != output_merkle_tree_pubkeys.len() { println!("{:?}", recipients); @@ -410,6 +569,7 @@ pub async fn compressed_transfer_test>( true, delegate_change_account_index, None, + token_22, ) .unwrap(); let sum_input_lamports = input_compressed_accounts @@ -502,6 +662,7 @@ pub async fn decompress_test>( output_merkle_tree_pubkey: &Pubkey, recipient_token_account: &Pubkey, transaction_params: Option, + is_token_22: bool, ) { let max_amount: u64 = input_compressed_accounts .iter() @@ -560,6 +721,7 @@ pub async fn decompress_test>( true, None, None, + is_token_22, ) .unwrap(); let output_merkle_tree_pubkeys = vec![*output_merkle_tree_pubkey]; @@ -634,6 +796,7 @@ pub async fn perform_compress_spl_token_account> token_account: &Pubkey, merkle_tree_pubkey: &Pubkey, remaining_amount: Option, + is_token_22: bool, ) -> Result<(), RpcError> { let pre_token_account_amount = spl_token::state::Account::unpack( &rpc.get_account(*token_account).await.unwrap().unwrap().data, @@ -649,6 +812,7 @@ pub async fn perform_compress_spl_token_account> mint, merkle_tree_pubkey, token_account, + is_token_22, ); let (event, _, _) = rpc .create_and_send_transaction_with_event::( @@ -702,6 +866,7 @@ pub async fn compress_test>( output_merkle_tree_pubkey: &Pubkey, sender_token_account: &Pubkey, transaction_params: Option, + is_token_22: bool, ) { let output_compressed_account = TokenTransferOutputData { amount, @@ -728,6 +893,7 @@ pub async fn compress_test>( true, None, None, + is_token_22, ) .unwrap(); let output_merkle_tree_pubkeys = vec![*output_merkle_tree_pubkey]; @@ -1254,6 +1420,7 @@ pub async fn burn_test>( burn_amount: u64, signer_is_delegate: bool, transaction_params: Option, + is_token_22: bool, ) { let ( input_compressed_account_hashes, @@ -1270,6 +1437,7 @@ pub async fn burn_test>( burn_amount, signer_is_delegate, BurnInstructionMode::Normal, + is_token_22, ) .await; let output_merkle_tree_pubkeys = vec![*change_account_merkle_tree; 1]; @@ -1391,6 +1559,7 @@ pub async fn create_burn_test_instruction>( burn_amount: u64, signer_is_delegate: bool, mode: BurnInstructionMode, + is_token_22: bool, ) -> (Vec<[u8; 32]>, Vec, Pubkey, u64, Instruction) { let input_compressed_account_hashes = input_compressed_accounts .iter() @@ -1445,6 +1614,7 @@ pub async fn create_burn_test_instruction>( mint, signer_is_delegate, burn_amount, + is_token_22, }; let input_amount_sum = input_compressed_accounts .iter()