spl_token_2022/extension/transfer_hook/
mod.rs

1#[cfg(feature = "serde-traits")]
2use serde::{Deserialize, Serialize};
3use {
4    crate::{
5        extension::{
6            BaseState, BaseStateWithExtensions, BaseStateWithExtensionsMut, Extension,
7            ExtensionType, PodStateWithExtensionsMut,
8        },
9        pod::PodAccount,
10    },
11    bytemuck::{Pod, Zeroable},
12    solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey},
13    spl_pod::{optional_keys::OptionalNonZeroPubkey, primitives::PodBool},
14};
15
16/// Instructions for the `TransferHook` extension
17pub mod instruction;
18/// Instruction processor for the `TransferHook` extension
19pub mod processor;
20
21/// Transfer hook extension data for mints.
22#[repr(C)]
23#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
25#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
26pub struct TransferHook {
27    /// Authority that can set the transfer hook program id
28    pub authority: OptionalNonZeroPubkey,
29    /// Program that authorizes the transfer
30    pub program_id: OptionalNonZeroPubkey,
31}
32
33/// Indicates that the tokens from this account belong to a mint with a transfer
34/// hook
35#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
36#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
37#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
38#[repr(transparent)]
39pub struct TransferHookAccount {
40    /// Flag to indicate that the account is in the middle of a transfer
41    pub transferring: PodBool,
42}
43
44impl Extension for TransferHook {
45    const TYPE: ExtensionType = ExtensionType::TransferHook;
46}
47
48impl Extension for TransferHookAccount {
49    const TYPE: ExtensionType = ExtensionType::TransferHookAccount;
50}
51
52/// Attempts to get the transfer hook program id from the TLV data, returning
53/// None if the extension is not found
54pub fn get_program_id<S: BaseState, BSE: BaseStateWithExtensions<S>>(
55    state: &BSE,
56) -> Option<Pubkey> {
57    state
58        .get_extension::<TransferHook>()
59        .ok()
60        .and_then(|e| Option::<Pubkey>::from(e.program_id))
61}
62
63/// Helper function to set the transferring flag before calling into transfer
64/// hook
65pub fn set_transferring<BSE: BaseStateWithExtensionsMut<S>, S: BaseState>(
66    account: &mut BSE,
67) -> Result<(), ProgramError> {
68    let account_extension = account.get_extension_mut::<TransferHookAccount>()?;
69    account_extension.transferring = true.into();
70    Ok(())
71}
72
73/// Helper function to unset the transferring flag after a transfer
74pub fn unset_transferring(account_info: &AccountInfo) -> Result<(), ProgramError> {
75    let mut account_data = account_info.data.borrow_mut();
76    let mut account = PodStateWithExtensionsMut::<PodAccount>::unpack(&mut account_data)?;
77    let account_extension = account.get_extension_mut::<TransferHookAccount>()?;
78    account_extension.transferring = false.into();
79    Ok(())
80}