Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 3 additions & 6 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ consolidate-commits = false
mollusk-svm = "0.4.0"
mollusk-svm-fuzz-fixture = "0.4.0"
num-traits = "0.2"
pinocchio = "0.9.2"
pinocchio = { version = "0.9.2", git = "https://github.com/anza-xyz/pinocchio.git", branch = "febo/precision-cap" }
solana-instruction = "2.3.0"
solana-program-error = "2.2.2"
solana-program-option = "2.2.1"
Expand Down
2 changes: 1 addition & 1 deletion p-interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ crate-type = ["rlib"]

[dependencies]
pinocchio = { workspace = true }
pinocchio-pubkey = "0.3"
pinocchio-pubkey = { version = "0.3", git = "https://github.com/anza-xyz/pinocchio.git", branch = "febo/precision-cap" }

[dev-dependencies]
strum = "0.27"
Expand Down
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", default-features = false,git = "https://github.com/anza-xyz/pinocchio.git", branch = "febo/precision-cap" }
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)],
);
}