amount_to_ui_amount.rs 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. use {
  2. super::{check_account_owner, MAX_FORMATTED_DIGITS, U64_BYTES},
  3. core::str::from_utf8_unchecked,
  4. pinocchio::{
  5. account_info::AccountInfo, program::set_return_data, program_error::ProgramError,
  6. ProgramResult,
  7. },
  8. pinocchio_log::logger::{Argument, Logger},
  9. spl_token_interface::{
  10. error::TokenError,
  11. state::{load, mint::Mint},
  12. },
  13. };
  14. pub fn process_amount_to_ui_amount(
  15. accounts: &[AccountInfo],
  16. instruction_data: &[u8],
  17. ) -> ProgramResult {
  18. let amount = if instruction_data.len() >= U64_BYTES {
  19. // SAFETY: The minimum size of the instruction data is `U64_BYTES` bytes.
  20. unsafe { u64::from_le_bytes(*(instruction_data.as_ptr() as *const [u8; U64_BYTES])) }
  21. } else {
  22. return Err(TokenError::InvalidInstruction.into());
  23. };
  24. let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
  25. check_account_owner(mint_info)?;
  26. // SAFETY: single immutable borrow to `mint_info` account data and
  27. // `load` validates that the mint is initialized.
  28. let mint = unsafe {
  29. load::<Mint>(mint_info.borrow_data_unchecked()).map_err(|_| TokenError::InvalidMint)?
  30. };
  31. let mut logger = Logger::<MAX_FORMATTED_DIGITS>::default();
  32. logger.append_with_args(amount, &[Argument::Precision(mint.decimals)]);
  33. // "Extract" the formatted string from the logger.
  34. //
  35. // SAFETY: the logger is guaranteed to be a valid UTF-8 string.
  36. let mut s = unsafe { from_utf8_unchecked(&logger) };
  37. if mint.decimals > 0 && s.contains('.') {
  38. let zeros_trimmed = s.trim_end_matches('0');
  39. s = zeros_trimmed.trim_end_matches('.');
  40. }
  41. set_return_data(s.as_bytes());
  42. Ok(())
  43. }