Browse Source

Add precision tests

febo 2 weeks ago
parent
commit
8876df817b

+ 39 - 1
p-token/tests/amount_to_ui_amount.rs

@@ -1,7 +1,12 @@
 mod setup;
 
 use {
-    setup::{mint, TOKEN_PROGRAM_ID},
+    mollusk_svm::result::Check,
+    setup::{
+        mint,
+        mollusk::{create_mint_account, mollusk},
+        TOKEN_PROGRAM_ID,
+    },
     solana_program_test::{tokio, ProgramTest},
     solana_pubkey::Pubkey,
     solana_signer::Signer,
@@ -45,3 +50,36 @@ async fn amount_to_ui_amount() {
 
     assert!(account.is_some());
 }
+
+#[test]
+fn amount_to_ui_amount_with_maximum_decimals() {
+    // Given a mint account with `u8::MAX` as decimals.
+
+    let mint = Pubkey::new_unique();
+    let mint_authority = Pubkey::new_unique();
+    let freeze_authority = Pubkey::new_unique();
+
+    let mint_account = create_mint_account(
+        mint_authority,
+        Some(freeze_authority),
+        u8::MAX,
+        &TOKEN_PROGRAM_ID,
+    );
+
+    // When we convert a 20 amount using the mint the transaction should succeed and
+    // return the correct UI amount.
+
+    let instruction =
+        spl_token::instruction::amount_to_ui_amount(&spl_token::ID, &mint, 20).unwrap();
+
+    // The expected UI amount is "0.000....002" without the trailing zeros.
+    let mut ui_amount = [b'0'; u8::MAX as usize + 1];
+    ui_amount[1] = b'.';
+    ui_amount[ui_amount.len() - 1] = b'2';
+
+    mollusk().process_and_validate_instruction(
+        &instruction,
+        &[(mint, mint_account)],
+        &[Check::success(), Check::return_data(&ui_amount)],
+    );
+}

+ 11 - 1
p-token/tests/setup/mint.rs

@@ -15,6 +15,16 @@ pub async fn initialize(
     mint_authority: Pubkey,
     freeze_authority: Option<Pubkey>,
     program_id: &Pubkey,
+) -> Result<Pubkey, ProgramError> {
+    initialize_with_decimals(context, mint_authority, freeze_authority, 4, program_id).await
+}
+
+pub async fn initialize_with_decimals(
+    context: &mut ProgramTestContext,
+    mint_authority: Pubkey,
+    freeze_authority: Option<Pubkey>,
+    decimals: u8,
+    program_id: &Pubkey,
 ) -> Result<Pubkey, ProgramError> {
     // Mint account keypair.
     let account = Keypair::new();
@@ -27,7 +37,7 @@ pub async fn initialize(
         &account.pubkey(),
         &mint_authority,
         freeze_authority.as_ref(),
-        4,
+        decimals,
     )
     .unwrap();
     // Switches the program id in case we are using a "custom" one.

+ 2 - 0
p-token/tests/setup/mod.rs

@@ -4,5 +4,7 @@ use solana_pubkey::Pubkey;
 pub mod account;
 #[allow(dead_code)]
 pub mod mint;
+#[allow(dead_code)]
+pub mod mollusk;
 
 pub const TOKEN_PROGRAM_ID: Pubkey = Pubkey::new_from_array(pinocchio_token_interface::program::ID);

+ 46 - 0
p-token/tests/setup/mollusk.rs

@@ -0,0 +1,46 @@
+use mollusk_svm::Mollusk;
+use pinocchio_token_interface::state::{load_mut_unchecked, mint::Mint};
+use solana_account::Account;
+use solana_pubkey::Pubkey;
+use solana_rent::Rent;
+use solana_sdk_ids::bpf_loader_upgradeable;
+
+use crate::setup::TOKEN_PROGRAM_ID;
+
+pub fn create_mint_account(
+    mint_authority: Pubkey,
+    freeze_authority: Option<Pubkey>,
+    decimals: u8,
+    program_owner: &Pubkey,
+) -> Account {
+    let space = size_of::<Mint>();
+    let lamports = Rent::default().minimum_balance(space);
+
+    let mut data: Vec<u8> = vec![0u8; space];
+    let mint = unsafe { load_mut_unchecked::<Mint>(data.as_mut_slice()).unwrap() };
+    mint.set_mint_authority(mint_authority.as_array());
+    if let Some(freeze_authority) = freeze_authority {
+        mint.set_freeze_authority(freeze_authority.as_array());
+    }
+    mint.set_initialized();
+    mint.decimals = decimals;
+
+    Account {
+        lamports,
+        data,
+        owner: *program_owner,
+        executable: false,
+        ..Default::default()
+    }
+}
+
+/// Creates a Mollusk instance with the default feature set.
+pub fn mollusk() -> Mollusk {
+    let mut mollusk = Mollusk::default();
+    mollusk.add_program(
+        &TOKEN_PROGRAM_ID,
+        "pinocchio_token_program",
+        &bpf_loader_upgradeable::id(),
+    );
+    mollusk
+}

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

@@ -1,6 +1,9 @@
 mod setup;
 
 use {
+    crate::setup::mollusk::{create_mint_account, mollusk},
+    core::str::from_utf8,
+    mollusk_svm::result::Check,
     setup::{mint, TOKEN_PROGRAM_ID},
     solana_program_test::{tokio, ProgramTest},
     solana_pubkey::Pubkey,
@@ -45,3 +48,38 @@ async fn ui_amount_to_amount() {
 
     assert!(account.is_some());
 }
+
+#[test]
+fn ui_amount_to_amount_with_maximum_decimals() {
+    // Given a mint account with `u8::MAX` as decimals.
+
+    let mint = Pubkey::new_unique();
+    let mint_authority = Pubkey::new_unique();
+    let freeze_authority = Pubkey::new_unique();
+
+    let mint_account = create_mint_account(
+        mint_authority,
+        Some(freeze_authority),
+        u8::MAX,
+        &TOKEN_PROGRAM_ID,
+    );
+
+    // String representing the ui value `0.000....002`
+    let mut ui_amount = [b'0'; u8::MAX as usize + 1];
+    ui_amount[1] = b'.';
+    ui_amount[ui_amount.len() - 1] = b'2';
+
+    let input = from_utf8(&ui_amount).unwrap();
+
+    // When we convert the ui amount using the mint, the transaction should
+    // succeed and return 20 as the amount.
+
+    let instruction =
+        spl_token::instruction::ui_amount_to_amount(&spl_token::ID, &mint, input).unwrap();
+
+    mollusk().process_and_validate_instruction(
+        &instruction,
+        &[(mint, mint_account)],
+        &[Check::success(), Check::return_data(&20u64.to_le_bytes())],
+    );
+}