1
0

burn.rs 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. use pinocchio::{
  2. account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
  3. };
  4. use token_interface::{
  5. error::TokenError,
  6. state::{account::Account, mint::Mint},
  7. };
  8. use crate::processor::{
  9. check_account_owner, is_owned_by_system_program_or_incinerator, validate_owner,
  10. };
  11. #[inline(always)]
  12. pub fn process_burn(
  13. program_id: &Pubkey,
  14. accounts: &[AccountInfo],
  15. amount: u64,
  16. expected_decimals: Option<u8>,
  17. ) -> ProgramResult {
  18. let [source_account_info, mint_info, authority_info, remaining @ ..] = accounts else {
  19. return Err(ProgramError::NotEnoughAccountKeys);
  20. };
  21. // Safety: There are no conflicting borrows – the source account is only borrowed once.
  22. let source_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
  23. source_account_info.borrow_mut_data_unchecked()
  24. })
  25. .map_err(|_error| ProgramError::InvalidAccountData)?;
  26. if source_account.is_frozen() {
  27. return Err(TokenError::AccountFrozen.into());
  28. }
  29. if source_account.is_native.is_some() {
  30. return Err(TokenError::NativeNotSupported.into());
  31. }
  32. // Ensure the source account has the sufficient amount. This is done before
  33. // the value is updated on the account.
  34. let updated_source_amount = u64::from(source_account.amount)
  35. .checked_sub(amount)
  36. .ok_or(TokenError::InsufficientFunds)?;
  37. // Safety: There are no conflicting borrows – the mint account is only borrowed once.
  38. let mint =
  39. bytemuck::try_from_bytes_mut::<Mint>(unsafe { mint_info.borrow_mut_data_unchecked() })
  40. .map_err(|_error| ProgramError::InvalidAccountData)?;
  41. if mint_info.key() != &source_account.mint {
  42. return Err(TokenError::MintMismatch.into());
  43. }
  44. if let Some(expected_decimals) = expected_decimals {
  45. if expected_decimals != mint.decimals {
  46. return Err(TokenError::MintDecimalsMismatch.into());
  47. }
  48. }
  49. if !is_owned_by_system_program_or_incinerator(&source_account.owner) {
  50. match source_account.delegate.as_ref() {
  51. Some(delegate) if authority_info.key() == delegate => {
  52. validate_owner(program_id, delegate, authority_info, remaining)?;
  53. let delegated_amount = u64::from(source_account.delegated_amount)
  54. .checked_sub(amount)
  55. .ok_or(TokenError::InsufficientFunds)?;
  56. source_account.delegated_amount = delegated_amount.into();
  57. if delegated_amount == 0 {
  58. source_account.delegate.clear();
  59. }
  60. }
  61. _ => {
  62. validate_owner(program_id, &source_account.owner, authority_info, remaining)?;
  63. }
  64. }
  65. }
  66. if amount == 0 {
  67. check_account_owner(program_id, source_account_info)?;
  68. check_account_owner(program_id, mint_info)?;
  69. }
  70. source_account.amount = updated_source_amount.into();
  71. let mint_supply = u64::from(mint.supply)
  72. .checked_sub(amount)
  73. .ok_or(TokenError::Overflow)?;
  74. mint.supply = mint_supply.into();
  75. Ok(())
  76. }