transfer.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. use pinocchio::{
  2. account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
  3. pubkey::Pubkey,
  4. };
  5. use crate::{
  6. error::TokenError,
  7. native_mint::is_native_mint,
  8. state::{account::Account, mint::Mint, PodCOption},
  9. };
  10. use super::{check_account_owner, validate_owner};
  11. pub fn process_transfer(
  12. program_id: &Pubkey,
  13. accounts: &[AccountInfo],
  14. amount: u64,
  15. expected_decimals: Option<u8>,
  16. ) -> ProgramResult {
  17. // Accounts expected depends on whether we have the mint `decimals` or not; when we have the
  18. // mint `decimals`, we expect the mint account to be present.
  19. let (
  20. source_account_info,
  21. expected_mint_info,
  22. destination_account_info,
  23. authority_info,
  24. remaning,
  25. ) = if let Some(decimals) = expected_decimals {
  26. let [source_account_info, mint_info, destination_account_info, authority_info, remaning @ ..] =
  27. accounts
  28. else {
  29. return Err(ProgramError::NotEnoughAccountKeys);
  30. };
  31. (
  32. source_account_info,
  33. Some((mint_info, decimals)),
  34. destination_account_info,
  35. authority_info,
  36. remaning,
  37. )
  38. } else {
  39. let [source_account_info, destination_account_info, authority_info, remaning @ ..] =
  40. accounts
  41. else {
  42. return Err(ProgramError::NotEnoughAccountKeys);
  43. };
  44. (
  45. source_account_info,
  46. None,
  47. destination_account_info,
  48. authority_info,
  49. remaning,
  50. )
  51. };
  52. // Validates source and destination accounts.
  53. let source_account_data = unsafe { source_account_info.unchecked_borrow_mut_data() };
  54. let source_account = bytemuck::from_bytes_mut::<Account>(source_account_data);
  55. let destination_account_data = unsafe { destination_account_info.unchecked_borrow_mut_data() };
  56. let destination_account = bytemuck::from_bytes_mut::<Account>(destination_account_data);
  57. if source_account.is_frozen() || destination_account.is_frozen() {
  58. return Err(TokenError::AccountFrozen.into());
  59. }
  60. // FEBO: Implicitly validates that the account has enough tokens by calculating the
  61. // remaining amount. The amount is only updated on the account if the transfer
  62. // is successful.
  63. let remaining_amount = u64::from_le_bytes(source_account.amount)
  64. .checked_sub(amount)
  65. .ok_or(TokenError::InsufficientFunds)?;
  66. if source_account.mint != destination_account.mint {
  67. return Err(TokenError::MintMismatch.into());
  68. }
  69. // Validates the mint information.
  70. if let Some((mint_info, decimals)) = expected_mint_info {
  71. if mint_info.key() != &source_account.mint {
  72. return Err(TokenError::MintMismatch.into());
  73. }
  74. let mint_data = mint_info.try_borrow_data()?;
  75. let mint = bytemuck::from_bytes::<Mint>(&mint_data);
  76. if decimals != mint.decimals {
  77. return Err(TokenError::MintDecimalsMismatch.into());
  78. }
  79. }
  80. let self_transfer = source_account_info.key() == destination_account_info.key();
  81. // Validates the authority (delegate or owner).
  82. if source_account.delegate.as_ref() == Some(authority_info.key()) {
  83. validate_owner(program_id, authority_info.key(), authority_info, remaning)?;
  84. let delegated_amount = u64::from_le_bytes(source_account.delegated_amount)
  85. .checked_sub(amount)
  86. .ok_or(TokenError::InsufficientFunds)?;
  87. if !self_transfer {
  88. source_account.delegated_amount = delegated_amount.to_le_bytes();
  89. if delegated_amount == 0 {
  90. source_account.delegate = PodCOption::from(None);
  91. }
  92. }
  93. } else {
  94. validate_owner(program_id, &source_account.owner, authority_info, remaning)?;
  95. }
  96. if self_transfer || amount == 0 {
  97. check_account_owner(program_id, source_account_info)?;
  98. check_account_owner(program_id, destination_account_info)?;
  99. // No need to move tokens around.
  100. return Ok(());
  101. }
  102. // FEBO: This was moved to the if statement above since we can skip the amount
  103. // manipulation if it is a self-transfer or the amount is zero.
  104. //
  105. // This check MUST occur just before the amounts are manipulated
  106. // to ensure self-transfers are fully validated
  107. /*
  108. if self_transfer {
  109. return Ok(());
  110. }
  111. */
  112. // Moves the tokens.
  113. source_account.amount = remaining_amount.to_le_bytes();
  114. let destination_amount = u64::from_le_bytes(destination_account.amount)
  115. .checked_add(amount)
  116. .ok_or(TokenError::Overflow)?;
  117. destination_account.amount = destination_amount.to_le_bytes();
  118. if is_native_mint(&source_account.mint) {
  119. let mut source_lamports = source_account_info.try_borrow_mut_lamports()?;
  120. *source_lamports = source_lamports
  121. .checked_sub(amount)
  122. .ok_or(TokenError::Overflow)?;
  123. let mut destination_lamports = destination_account_info.try_borrow_mut_lamports()?;
  124. *destination_lamports = destination_lamports
  125. .checked_add(amount)
  126. .ok_or(TokenError::Overflow)?;
  127. }
  128. Ok(())
  129. }