burn.rs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
  2. use spl_token_interface::{
  3. error::TokenError,
  4. state::{account::Account, load_mut, mint::Mint},
  5. };
  6. use crate::processor::{check_account_owner, validate_owner};
  7. #[inline(always)]
  8. pub fn process_burn(
  9. accounts: &[AccountInfo],
  10. amount: u64,
  11. expected_decimals: Option<u8>,
  12. ) -> ProgramResult {
  13. let [source_account_info, mint_info, authority_info, remaining @ ..] = accounts else {
  14. return Err(ProgramError::NotEnoughAccountKeys);
  15. };
  16. // SAFETY: single mutable borrow to `source_account_info` account data and
  17. // `load_mut` validates that the account is initialized.
  18. let source_account =
  19. unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
  20. if source_account.is_frozen() {
  21. return Err(TokenError::AccountFrozen.into());
  22. }
  23. if source_account.is_native() {
  24. return Err(TokenError::NativeNotSupported.into());
  25. }
  26. // Ensure the source account has the sufficient amount. This is done before
  27. // the value is updated on the account.
  28. let updated_source_amount = source_account
  29. .amount()
  30. .checked_sub(amount)
  31. .ok_or(TokenError::InsufficientFunds)?;
  32. // SAFETY: single mutable borrow to `mint_info` account data and
  33. // `load_mut` validates that the mint is initialized.
  34. let mint = unsafe { load_mut::<Mint>(mint_info.borrow_mut_data_unchecked())? };
  35. if mint_info.key() != &source_account.mint {
  36. return Err(TokenError::MintMismatch.into());
  37. }
  38. if let Some(expected_decimals) = expected_decimals {
  39. if expected_decimals != mint.decimals {
  40. return Err(TokenError::MintDecimalsMismatch.into());
  41. }
  42. }
  43. if !source_account.is_owned_by_system_program_or_incinerator() {
  44. match source_account.delegate() {
  45. Some(delegate) if authority_info.key() == delegate => {
  46. validate_owner(delegate, authority_info, remaining)?;
  47. let delegated_amount = source_account
  48. .delegated_amount()
  49. .checked_sub(amount)
  50. .ok_or(TokenError::InsufficientFunds)?;
  51. source_account.set_delegated_amount(delegated_amount);
  52. if delegated_amount == 0 {
  53. source_account.clear_delegate();
  54. }
  55. }
  56. _ => {
  57. validate_owner(&source_account.owner, authority_info, remaining)?;
  58. }
  59. }
  60. }
  61. // Updates the source account and mint supply.
  62. if amount == 0 {
  63. check_account_owner(source_account_info)?;
  64. check_account_owner(mint_info)?;
  65. } else {
  66. source_account.set_amount(updated_source_amount);
  67. let mint_supply = mint
  68. .supply()
  69. .checked_sub(amount)
  70. .ok_or(TokenError::Overflow)?;
  71. mint.set_supply(mint_supply);
  72. }
  73. Ok(())
  74. }