Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion p-token/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ logging = []

[dependencies]
pinocchio = { workspace = true }
pinocchio-log = { version = "0.5", default-features = false }
pinocchio-log = { version = "0.5.1", default-features = false }
pinocchio-token-interface = { version = "^0", path = "../p-interface" }

[dev-dependencies]
Expand Down
85 changes: 84 additions & 1 deletion p-token/tests/amount_to_ui_amount.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -45,3 +50,81 @@ 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)],
);
}

#[test]
fn amount_to_ui_amount_with_u64_max() {
// 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 an u64::MAX 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, u64::MAX).unwrap();

// The expected UI amount is a `u64::MAX` with 255 decimal places.
// - 2 digits for `0.`
// - 255 digits for the maximum decimals.
let mut ui_amount = [b'0'; u8::MAX as usize + 2];
ui_amount[1] = b'.';

let mut offset = ui_amount.len();
let mut value = u64::MAX;

while value > 0 {
let remainder = value % 10;
value /= 10;
offset -= 1;

ui_amount[offset] = b'0' + (remainder as u8);
}

mollusk().process_and_validate_instruction(
&instruction,
&[(mint, mint_account)],
&[Check::success(), Check::return_data(&ui_amount)],
);
}
12 changes: 11 additions & 1 deletion p-token/tests/setup/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions p-token/tests/setup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
47 changes: 47 additions & 0 deletions p-token/tests/setup/mollusk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use {
crate::setup::TOKEN_PROGRAM_ID,
mollusk_svm::Mollusk,
pinocchio_token_interface::state::{load_mut_unchecked, mint::Mint},
solana_account::Account,
solana_pubkey::Pubkey,
solana_rent::Rent,
solana_sdk_ids::bpf_loader_upgradeable,
};

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
}
71 changes: 71 additions & 0 deletions p-token/tests/ui_amount_to_amount.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
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_error::ProgramError,
solana_program_test::{tokio, ProgramTest},
solana_pubkey::Pubkey,
solana_signer::Signer,
Expand Down Expand Up @@ -45,3 +49,70 @@ 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())],
);
}

#[test]
fn fail_ui_amount_to_amount_with_invalid_ui_amount() {
// 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 `2.0`
let ui_amount = [b'2', b'.', b'0'];
let input = from_utf8(&ui_amount).unwrap();

// When we try to convert the ui amount using the mint, the transaction should
// fail with an error since the resulting value does not fit in an `u64`.

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::err(ProgramError::InvalidArgument)],
);
}