Pārlūkot izejas kodu

p-token: Add `batch` instruction (#35)

* Update workspace

* Rename interface crate

* Fix spelling

* [wip]: Fix review comments

* A few more fixes

* Add batch instruction

* Add test for batch

* Clean up comments

* Fix spelling

* Fix merge

* Remove test-sbf feature

* Fix review comments
Fernando Otero 7 mēneši atpakaļ
vecāks
revīzija
baa5918cce

+ 19 - 2
interface/src/instruction.rs

@@ -477,6 +477,24 @@ pub enum TokenInstruction {
     ///
     ///   - `&str` The `ui_amount` of tokens to reformat.
     UiAmountToAmount,
+
+    /// Executes a batch of instructions. The instructions to be executed are specified
+    /// in sequence on the instruction data. Each instruction provides:
+    ///   - `u8`: number of accounts
+    ///   - `u8`: instruction data length (includes the discriminator)
+    ///   - `u8`: instruction discriminator
+    ///   - `[u8]`: instruction data
+    ///
+    /// Accounts follow a similar pattern, where accounts for each instruction are
+    /// specified in sequence. Therefore, the number of accounts expected by this
+    /// instruction is variable, i.e., it depends on the instructions provided.
+    ///
+    /// Both the number of accounts and instruction data length are used to identify
+    /// the slice of accounts and instruction data for each instruction.
+    ///
+    /// Note that it is not sound to have a `batch` instruction that contains other
+    /// `batch` instruction; an error will be raised when this is detected.
+    Batch = 255,
     // Any new variants also need to be added to program-2022 `TokenInstruction`, so that the
     // latter remains a superset of this instruction set. New variants also need to be added to
     // token/js/src/instructions/types.ts to maintain @solana/spl-token compatibility
@@ -485,11 +503,10 @@ pub enum TokenInstruction {
 impl TryFrom<u8> for TokenInstruction {
     type Error = ProgramError;
 
-    #[inline(always)]
     fn try_from(value: u8) -> Result<Self, Self::Error> {
         match value {
             // SAFETY: `value` is guaranteed to be in the range of the enum variants.
-            0..=24 => Ok(unsafe { core::mem::transmute::<u8, TokenInstruction>(value) }),
+            0..=24 | 255 => Ok(unsafe { core::mem::transmute::<u8, TokenInstruction>(value) }),
             _ => Err(ProgramError::InvalidInstructionData),
         }
     }

+ 0 - 1
p-token/Cargo.toml

@@ -13,7 +13,6 @@ crate-type = ["cdylib"]
 
 [features]
 logging = []
-test-sbf = []
 
 [dependencies]
 pinocchio = { version = "0.7", git = "https://github.com/febo/pinocchio.git", branch = "febo/close-unstable" }

+ 83 - 57
p-token/src/entrypoint.rs

@@ -2,7 +2,6 @@ use pinocchio::{
     account_info::AccountInfo, default_panic_handler, no_allocator, program_entrypoint,
     program_error::ProgramError, pubkey::Pubkey, ProgramResult,
 };
-use spl_token_interface::instruction::TokenInstruction;
 
 use crate::processor::*;
 
@@ -14,218 +13,245 @@ default_panic_handler!();
 
 /// Process an instruction.
 ///
+/// In the first stage, the entrypoint checks the discriminator of the instruction data
+/// to determine whether the instruction is a "batch" instruction or a "regular" instruction.
+/// This avoids nesting of "batch" instructions, since it is not sound to have a "batch"
+/// instruction inside another "batch" instruction.
+#[inline(always)]
+pub fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let [discriminator, remaining @ ..] = instruction_data else {
+        return Err(ProgramError::InvalidInstructionData);
+    };
+
+    if *discriminator == 255 {
+        // 255 - Batch
+        #[cfg(feature = "logging")]
+        pinocchio::msg!("Instruction: Batch");
+
+        return process_batch(accounts, remaining);
+    }
+
+    inner_process_instruction(accounts, instruction_data)
+}
+
+/// Process a "regular" instruction.
+///
 /// The processor of the token program is divided into two parts to reduce the overhead
 /// of having a large `match` statement. The first part of the processor handles the
 /// most common instructions, while the second part handles the remaining instructions.
+///
 /// The rationale is to reduce the overhead of making multiple comparisons for popular
 /// instructions.
 ///
-/// Instructions on the first part of the processor:
+/// Instructions on the first part of the inner processor:
 ///
-/// - `0`: `InitializeMint`
-/// - `3`:  `Transfer`
-/// - `7`:  `MintTo`
-/// - `9`:  `CloseAccount`
+/// -  `0`: `InitializeMint`
+/// -  `1`: `InitializeAccount`
+/// -  `3`: `Transfer`
+/// -  `7`: `MintTo`
+/// -  `9`: `CloseAccount`
+/// - `16`: `InitializeAccount2`
 /// - `18`: `InitializeAccount3`
 /// - `20`: `InitializeMint2`
 #[inline(always)]
-pub fn process_instruction(
-    _program_id: &Pubkey,
+pub(crate) fn inner_process_instruction(
     accounts: &[AccountInfo],
     instruction_data: &[u8],
 ) -> ProgramResult {
-    let (discriminator, instruction_data) = instruction_data
-        .split_first()
-        .ok_or(ProgramError::InvalidInstructionData)?;
-    let instruction = TokenInstruction::try_from(*discriminator)?;
+    let [discriminator, instruction_data @ ..] = instruction_data else {
+        return Err(ProgramError::InvalidInstructionData);
+    };
 
-    match instruction {
+    match *discriminator {
         // 0 - InitializeMint
-        TokenInstruction::InitializeMint => {
+        0 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeMint");
 
             process_initialize_mint(accounts, instruction_data)
         }
+        // 1 - InitializeAccount
+        1 => {
+            #[cfg(feature = "logging")]
+            pinocchio::msg!("Instruction: InitializeAccount");
 
+            process_initialize_account(accounts)
+        }
         // 3 - Transfer
-        TokenInstruction::Transfer => {
+        3 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Transfer");
 
             process_transfer(accounts, instruction_data)
         }
         // 7 - MintTo
-        TokenInstruction::MintTo => {
+        7 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: MintTo");
 
             process_mint_to(accounts, instruction_data)
         }
         // 9 - CloseAccount
-        TokenInstruction::CloseAccount => {
+        9 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: CloseAccount");
 
             process_close_account(accounts)
         }
+        // 16 - InitializeAccount2
+        16 => {
+            #[cfg(feature = "logging")]
+            pinocchio::msg!("Instruction: InitializeAccount2");
+
+            process_initialize_account2(accounts, instruction_data)
+        }
         // 18 - InitializeAccount3
-        TokenInstruction::InitializeAccount3 => {
+        18 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeAccount3");
 
             process_initialize_account3(accounts, instruction_data)
         }
         // 20 - InitializeMint2
-        TokenInstruction::InitializeMint2 => {
+        20 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeMint2");
 
             process_initialize_mint2(accounts, instruction_data)
         }
-        _ => process_remaining_instruction(accounts, instruction_data, instruction),
+        d => inner_process_remaining_instruction(accounts, instruction_data, d),
     }
 }
 
-/// Process the remaining instructions.
+/// Process a remaining "regular" instruction.
 ///
-/// This function is called by the `process_instruction` function if the discriminator
+/// This function is called by the [`inner_process_instruction`] function if the discriminator
 /// does not match any of the common instructions. This function is used to reduce the
-/// overhead of having a large `match` statement in the `process_instruction` function.
-fn process_remaining_instruction(
+/// overhead of having a large `match` statement in the [`inner_process_instruction`] function.
+fn inner_process_remaining_instruction(
     accounts: &[AccountInfo],
     instruction_data: &[u8],
-    instruction: TokenInstruction,
+    discriminator: u8,
 ) -> ProgramResult {
-    match instruction {
-        // 1 - InitializeAccount
-        TokenInstruction::InitializeAccount => {
-            #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: InitializeAccount");
-
-            process_initialize_account(accounts)
-        }
+    match discriminator {
         // 2 - InitializeMultisig
-        TokenInstruction::InitializeMultisig => {
+        2 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeMultisig");
 
             process_initialize_multisig(accounts, instruction_data)
         }
         // 4 - Approve
-        TokenInstruction::Approve => {
+        4 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Approve");
 
             process_approve(accounts, instruction_data)
         }
         // 5 - Revoke
-        TokenInstruction::Revoke => {
+        5 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Revoke");
 
             process_revoke(accounts, instruction_data)
         }
         // 6 - SetAuthority
-        TokenInstruction::SetAuthority => {
+        6 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: SetAuthority");
 
             process_set_authority(accounts, instruction_data)
         }
         // 8 - Burn
-        TokenInstruction::Burn => {
+        8 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Burn");
 
             process_burn(accounts, instruction_data)
         }
         // 10 - FreezeAccount
-        TokenInstruction::FreezeAccount => {
+        10 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: FreezeAccount");
 
             process_freeze_account(accounts)
         }
         // 11 - ThawAccount
-        TokenInstruction::ThawAccount => {
+        11 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: ThawAccount");
 
             process_thaw_account(accounts)
         }
         // 12 - TransferChecked
-        TokenInstruction::TransferChecked => {
+        12 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: TransferChecked");
 
             process_transfer_checked(accounts, instruction_data)
         }
         // 13 - ApproveChecked
-        TokenInstruction::ApproveChecked => {
+        13 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: ApproveChecked");
 
             process_approve_checked(accounts, instruction_data)
         }
         // 14 - MintToChecked
-        TokenInstruction::MintToChecked => {
+        14 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: MintToChecked");
 
             process_mint_to_checked(accounts, instruction_data)
         }
         // 15 - BurnChecked
-        TokenInstruction::BurnChecked => {
+        15 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: BurnChecked");
 
             process_burn_checked(accounts, instruction_data)
         }
-        // 16 - InitializeAccount2
-        TokenInstruction::InitializeAccount2 => {
-            #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: InitializeAccount2");
-
-            process_initialize_account2(accounts, instruction_data)
-        }
         // 17 - SyncNative
-        TokenInstruction::SyncNative => {
+        17 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: SyncNative");
 
             process_sync_native(accounts)
         }
         // 19 - InitializeMultisig2
-        TokenInstruction::InitializeMultisig2 => {
+        19 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeMultisig2");
 
             process_initialize_multisig2(accounts, instruction_data)
         }
         // 21 - GetAccountDataSize
-        TokenInstruction::GetAccountDataSize => {
+        21 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: GetAccountDataSize");
 
             process_get_account_data_size(accounts)
         }
         // 22 - InitializeImmutableOwner
-        TokenInstruction::InitializeImmutableOwner => {
+        22 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeImmutableOwner");
 
             process_initialize_immutable_owner(accounts)
         }
         // 23 - AmountToUiAmount
-        TokenInstruction::AmountToUiAmount => {
+        23 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: AmountToUiAmount");
 
             process_amount_to_ui_amount(accounts, instruction_data)
         }
         // 24 - UiAmountToAmount
-        TokenInstruction::UiAmountToAmount => {
+        24 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: UiAmountToAmount");
 

+ 57 - 0
p-token/src/processor/batch.rs

@@ -0,0 +1,57 @@
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
+
+use crate::entrypoint::inner_process_instruction;
+
+/// The size of the batch instruction header.
+///
+/// The header of each instruction consists of two `u8` values:
+///  * number of the accounts
+///  * length of the instruction data
+const IX_HEADER_SIZE: usize = 2;
+
+pub fn process_batch(mut accounts: &[AccountInfo], mut instruction_data: &[u8]) -> ProgramResult {
+    loop {
+        // Validates the instruction data and accounts offset.
+
+        if instruction_data.len() < IX_HEADER_SIZE {
+            // The instruction data must have at least two bytes.
+            return Err(ProgramError::InvalidInstructionData);
+        }
+
+        // SAFETY: The instruction data is guaranteed to have at least two bytes (header)
+        // + one byte (discriminator).
+        let expected_accounts = unsafe { *instruction_data.get_unchecked(0) as usize };
+        let data_offset = IX_HEADER_SIZE + unsafe { *instruction_data.get_unchecked(1) as usize };
+
+        if instruction_data.len() < data_offset || data_offset == IX_HEADER_SIZE {
+            return Err(ProgramError::InvalidInstructionData);
+        }
+
+        if accounts.len() < expected_accounts {
+            return Err(ProgramError::NotEnoughAccountKeys);
+        }
+
+        // Process the instruction.
+
+        // SAFETY: The instruction data and accounts lengths are already validated so all
+        // slices are guaranteed to be valid.
+        let (ix_accounts, ix_data) = unsafe {
+            (
+                accounts.get_unchecked(..expected_accounts),
+                instruction_data.get_unchecked(IX_HEADER_SIZE..data_offset),
+            )
+        };
+
+        inner_process_instruction(ix_accounts, ix_data)?;
+
+        if data_offset == instruction_data.len() {
+            // The batch is complete.
+            break;
+        }
+
+        accounts = &accounts[expected_accounts..];
+        instruction_data = &instruction_data[data_offset..];
+    }
+
+    Ok(())
+}

+ 2 - 0
p-token/src/processor/mod.rs

@@ -16,6 +16,7 @@ use spl_token_interface::{
 pub mod amount_to_ui_amount;
 pub mod approve;
 pub mod approve_checked;
+pub mod batch;
 pub mod burn;
 pub mod burn_checked;
 pub mod close_account;
@@ -44,6 +45,7 @@ pub mod shared;
 pub use amount_to_ui_amount::process_amount_to_ui_amount;
 pub use approve::process_approve;
 pub use approve_checked::process_approve_checked;
+pub use batch::process_batch;
 pub use burn::process_burn;
 pub use burn_checked::process_burn_checked;
 pub use close_account::process_close_account;

+ 0 - 2
p-token/tests/amount_to_ui_amount.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/approve.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/approve_checked.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 221 - 0
p-token/tests/batch.rs

@@ -0,0 +1,221 @@
+mod setup;
+
+use crate::setup::TOKEN_PROGRAM_ID;
+use solana_program_test::{tokio, ProgramTest};
+use solana_sdk::{
+    instruction::{AccountMeta, Instruction},
+    program_error::ProgramError,
+    program_pack::Pack,
+    pubkey::Pubkey,
+    signature::{Keypair, Signer},
+    system_instruction,
+    transaction::Transaction,
+};
+
+fn batch_instruction(instructions: Vec<Instruction>) -> Result<Instruction, ProgramError> {
+    // Create a `Vec` of ordered `AccountMeta`s
+    let mut accounts: Vec<AccountMeta> = vec![];
+    // Start with the batch discriminator
+    let mut data: Vec<u8> = vec![0xff];
+
+    for instruction in instructions {
+        // Error out on non-token IX.
+        if instruction.program_id.ne(&spl_token::ID) {
+            return Err(ProgramError::IncorrectProgramId);
+        }
+
+        data.push(instruction.accounts.len() as u8);
+        data.push(instruction.data.len() as u8);
+
+        data.extend_from_slice(&instruction.data);
+        accounts.extend_from_slice(&instruction.accounts);
+    }
+
+    Ok(Instruction {
+        program_id: spl_token::ID,
+        data,
+        accounts,
+    })
+}
+
+#[test_case::test_case(TOKEN_PROGRAM_ID ; "p-token")]
+#[tokio::test]
+async fn batch(token_program: Pubkey) {
+    let context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
+        .start_with_context()
+        .await;
+
+    let rent = context.banks_client.get_rent().await.unwrap();
+
+    let mint_len = spl_token::state::Mint::LEN;
+    let mint_rent = rent.minimum_balance(mint_len);
+
+    let account_len = spl_token::state::Account::LEN;
+    let account_rent = rent.minimum_balance(account_len);
+
+    // Create a mint
+    let mint_a = Keypair::new();
+    let mint_authority = Keypair::new();
+    let create_mint_a = system_instruction::create_account(
+        &context.payer.pubkey(),
+        &mint_a.pubkey(),
+        mint_rent,
+        mint_len as u64,
+        &token_program,
+    );
+    let initialize_mint_ix = spl_token::instruction::initialize_mint(
+        &token_program,
+        &mint_a.pubkey(),
+        &mint_authority.pubkey(),
+        None,
+        6,
+    )
+    .unwrap();
+
+    // Create a mint 2 with a freeze authority
+    let mint_b = Keypair::new();
+    let freeze_authority = Pubkey::new_unique();
+    let create_mint_b = system_instruction::create_account(
+        &context.payer.pubkey(),
+        &mint_b.pubkey(),
+        mint_rent,
+        mint_len as u64,
+        &token_program,
+    );
+    let initialize_mint_with_freeze_authority_ix = spl_token::instruction::initialize_mint2(
+        &token_program,
+        &mint_b.pubkey(),
+        &mint_authority.pubkey(),
+        Some(&freeze_authority),
+        6,
+    )
+    .unwrap();
+
+    // Create 2 token accounts for mint A and 1 for mint B
+    let owner_a = Keypair::new();
+    let owner_b = Keypair::new();
+    let owner_a_ta_a = Keypair::new();
+    let owner_b_ta_a = Keypair::new();
+
+    let create_owner_a_ta_a = system_instruction::create_account(
+        &context.payer.pubkey(),
+        &owner_a_ta_a.pubkey(),
+        account_rent,
+        account_len as u64,
+        &token_program,
+    );
+    let create_owner_b_ta_a = system_instruction::create_account(
+        &context.payer.pubkey(),
+        &owner_b_ta_a.pubkey(),
+        account_rent,
+        account_len as u64,
+        &token_program,
+    );
+    let intialize_owner_a_ta_a = spl_token::instruction::initialize_account3(
+        &token_program,
+        &owner_a_ta_a.pubkey(),
+        &mint_a.pubkey(),
+        &owner_a.pubkey(),
+    )
+    .unwrap();
+    let intialize_owner_b_ta_a = spl_token::instruction::initialize_account3(
+        &token_program,
+        &owner_b_ta_a.pubkey(),
+        &mint_a.pubkey(),
+        &owner_b.pubkey(),
+    )
+    .unwrap();
+
+    // Mint Token A to Owner A
+    let mint_token_a_to_owner_a = spl_token::instruction::mint_to(
+        &token_program,
+        &mint_a.pubkey(),
+        &owner_a_ta_a.pubkey(),
+        &mint_authority.pubkey(),
+        &[],
+        1_000_000,
+    )
+    .unwrap();
+
+    // Transfer Token A from Owner A to Owner B
+    let transfer_token_a_to_owner_b = spl_token::instruction::transfer(
+        &token_program,
+        &owner_a_ta_a.pubkey(),
+        &owner_b_ta_a.pubkey(),
+        &owner_a.pubkey(),
+        &[],
+        1_000_000,
+    )
+    .unwrap();
+
+    // Close Token A
+    let close_owner_a_ta_a = spl_token::instruction::close_account(
+        &token_program,
+        &owner_a_ta_a.pubkey(),
+        &owner_a.pubkey(),
+        &owner_a.pubkey(),
+        &[],
+    )
+    .unwrap();
+
+    let batch_ix = batch_instruction(vec![
+        initialize_mint_ix,
+        initialize_mint_with_freeze_authority_ix,
+        intialize_owner_a_ta_a,
+        intialize_owner_b_ta_a,
+        mint_token_a_to_owner_a,
+        transfer_token_a_to_owner_b,
+        close_owner_a_ta_a,
+    ])
+    .unwrap();
+
+    let tx = Transaction::new_signed_with_payer(
+        &[
+            create_mint_a,
+            create_mint_b,
+            create_owner_a_ta_a,
+            create_owner_b_ta_a,
+            batch_ix,
+        ],
+        Some(&context.payer.pubkey()),
+        &vec![
+            &context.payer,
+            &mint_a,
+            &mint_b,
+            &owner_a_ta_a,
+            &owner_b_ta_a,
+            &mint_authority,
+            &owner_a,
+        ],
+        context.last_blockhash,
+    );
+    context.banks_client.process_transaction(tx).await.unwrap();
+
+    let mint_a_account = context
+        .banks_client
+        .get_account(mint_a.pubkey())
+        .await
+        .unwrap();
+    assert!(mint_a_account.is_some());
+    let mint_a_account = spl_token::state::Mint::unpack(&mint_a_account.unwrap().data).unwrap();
+    assert_eq!(mint_a_account.supply, 1000000);
+
+    let mint_b_account = context
+        .banks_client
+        .get_account(mint_b.pubkey())
+        .await
+        .unwrap();
+    assert!(mint_b_account.is_some());
+    let mint_b_account = spl_token::state::Mint::unpack(&mint_b_account.unwrap().data).unwrap();
+    assert_eq!(mint_b_account.supply, 0);
+
+    let owner_b_ta_a_account = context
+        .banks_client
+        .get_account(owner_b_ta_a.pubkey())
+        .await
+        .unwrap();
+    assert!(owner_b_ta_a_account.is_some());
+    let owner_b_ta_a_account =
+        spl_token::state::Account::unpack(&owner_b_ta_a_account.unwrap().data).unwrap();
+    assert_eq!(owner_b_ta_a_account.amount, 1000000);
+}

+ 0 - 2
p-token/tests/burn.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/burn_checked.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/close_account.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/freeze_account.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/initialize_account.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/initialize_account2.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/initialize_account3.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/initialize_mint.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use std::mem::size_of;

+ 0 - 2
p-token/tests/initialize_mint2.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use std::mem::size_of;

+ 0 - 2
p-token/tests/initialize_multisig.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::TOKEN_PROGRAM_ID;

+ 0 - 2
p-token/tests/initialize_multisig2.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::TOKEN_PROGRAM_ID;

+ 0 - 2
p-token/tests/mint_to.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/mint_to_checked.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/revoke.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/set_authority.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/thaw_account.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/transfer.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/transfer_checked.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{account, mint, TOKEN_PROGRAM_ID};

+ 0 - 2
p-token/tests/ui_amount_to_amount.rs

@@ -1,5 +1,3 @@
-#![cfg(feature = "test-sbf")]
-
 mod setup;
 
 use setup::{mint, TOKEN_PROGRAM_ID};

+ 1 - 1
package.json

@@ -25,7 +25,7 @@
     "rust:publish": "zx ./scripts/rust/publish.mjs",
     "rust:semver": "cargo semver-checks",
     "p-token:build": "zx ./scripts/rust/build-sbf.mjs p-token",
-    "p-token:test": "zx ./scripts/rust/test-sbf.mjs p-token"
+    "p-token:test": "zx ./scripts/rust/test.mjs p-token"
   },
   "devDependencies": {
     "@codama/renderers-js": "^1.2.7",