本文详细介绍了Solana Token Program中的Transfer Hook扩展,该功能允许开发者在代币转移时执行自定义逻辑。文章涵盖了Transfer Hook的基本概念,使用方法,以及如何创建相应的程序结构,提供了代码示例和最佳实践,是对Solana生态中代币管理功能的重要补充。
Solana Token Program 是在 Solana 区块链上创建和管理代币的标准。在 2022 年,Solana Labs 发布了一组新功能,Token Extensions,允许对任何特定代币的功能进行自定义。
其中一个扩展是 Transfer Hook,它允许开发人员在代币转移时添加自定义逻辑。在本指南中,我们将讨论 Transfer Hook,工作原理,以及如何在你的 Solana 代币程序中使用它。
在阅读本指南之前,建议你对以下主题有一定了解:
Transfer hooks 要求在代币转移发生时执行自定义逻辑。例如,你可能希望:
使用 transfer hooks 的机会是无穷无尽的——这完全取决于你的想象力和项目的需求。具有 Transfer Hook 的代币有三个主要元素:
fallback
函数来处理 transfer hook。让我们仔细看看这些元素。
要使用 transfer hooks,必须使用 TOKEN_2022_PROGRAM_ID
创建 SPL 代币(因为扩展在之前版本的代币程序上不起作用)。像所有扩展一样,transfer hook 扩展必须在代币创建时初始化。
transfer hook 扩展本身相当简单,它由两个部分组成:
solana-program-library/token/program-2022/src/extension/transfer_hook/instruction.rs
pub struct InitializeInstructionData {
/// 可以更新程序 ID 的账户的公钥
pub authority: OptionalNonZeroPubkey,
/// 在转移期间执行逻辑的程序 ID
pub program_id: OptionalNonZeroPubkey,
}
_来源:GitHub - Solana SPL Token Program_
必须传递的程序必须包括在代币转移发生时将被调用的 transfer hook 指令。复杂性和乐趣来自你在 hook 中的自定义逻辑。我们稍后将讨论这个,但首先,我们需要考虑 transfer hook 所需的账户。接下来,让我们看看这个。
每次代币转移(无论你是否使用 transfer hooks)都需要几个账户:
Transfer hooks 至少需要一个额外的账户,即 ExtraAccountMetaList
账户。该账户存储有关任何必须在运行时传递给事务以执行 transfer hook 的额外账户的信息。该账户基于字符串字面量 "extra-account-metas" 和铸造的公钥进行播种,并使用特定 transfer hook 程序的程序 ID:
const [pda] = PublicKey.findProgramAddressSync(
[Buffer.from("extra-account-metas"), mint.publicKey.toBuffer()],
program.programId, // transfer hook 程序 ID
);
这些账户一起创建了一个 TransferHook
上下文,该上下文被传递到 transfer hook 程序指令中。例如:
#[derive(Accounts)]
pub struct TransferHook<'info> {
#[account(\
token::mint = mint,\
token::authority = owner,\
)]
pub source_token: InterfaceAccount<'info, TokenAccount>,
pub mint: InterfaceAccount<'info, Mint>,
#[account(\
token::mint = mint,\
)]
pub destination_token: InterfaceAccount<'info, TokenAccount>,
/// 检查:源代币账户所有者,可以是 SystemAccount 或 PDA,由另一个程序拥有
pub owner: UncheckedAccount<'info>,
/// CHECK:ExtraAccountMetaList 账户,
#[account(\
seeds = [b"extra-account-metas", mint.key().as_ref()],\
bump\
)]
pub extra_account_meta_list: UncheckedAccount<'info>,
// 在此添加任何额外账户 👇
}
Transfer hook 是在代币转移发生时调用的程序指令。该指令称为 "hook",快速执行时与代币转移原子执行。这意味着如果 hook 失败,则转移也会失败。Transfer hooks 能够执行任何数量的操作以强制执行业务逻辑,例如要求用户完成 KYC 后才能转移代币,或添加附加功能到代币转移,例如更新一个单独的账户以记录转移详细信息。让我们看看几个简单的示例,以了解 transfer hooks 的工作原理。
以下是一个检查用户是否被授权进行转移的示例:
#[error_code]
pub enum MyError {
#[msg("用户未被授权")]
Unauthorized,
}
#[interface(spl_transfer_hook_interface::execute)]
pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> {
let authorized = ctx.accounts.user.authorized;
if !authorized {
return err!(MyError::Unauthorized);
}
Ok(())
}
以下是另一个示例,检查转移是否在预定义的交易时间范围内进行:
#[error_code]
pub enum MyError {
#[msg("超出允许的交易时间")]
OutsideTradingHours,
}
#[interface(spl_transfer_hook_interface::execute)]
pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> {
let timestamp = Clock::get()?.unix_timestamp;
let day = Clock::get()?.days_from_unix_epoch % 7;
let hour = (timestamp % 86400) / 3600;
let is_trading_hours = day < 5 && hour >= 5 && hour < 17;
if !is_trading_hours {
return err!(MyError::OutsideTradingHours);
}
Ok(())
}
在上述每个示例中,如果条件不满足,转移将失败。这就是 transfer hooks 的力量——它们允许你在代币转移上强制执行自定义逻辑。请注意,#[interface]
宏是在 Anchor 0.30.0 中添加的,用于覆盖 Anchor 的默认指令区分符,以使用接口指令的区分符(因此你无需使用 fallback
函数)。
如果你使用的是旧版本的 Anchor(在 0.30.0 发行版本之前),你还需要在程序中包含 fallback
函数以处理 transfer hook。此函数是由于本机 Solana 程序和 Anchor 程序处理指令区分符的方式有所不同而要求的。
// fallback 指令处理程序,作为对抗 anchor 指令区分符检查的变通方法
pub fn fallback<'info>(
program_id: &Pubkey,
accounts: &'info [AccountInfo<'info>],
data: &[u8],
) -> Result<()> {
let instruction = TransferHookInstruction::unpack(data)?;
// 匹配指令区分符以获得 transfer hook 接口执行指令
// token2022 程序 CPI 在代币转移时调用此指令
match instruction {
TransferHookInstruction::Execute { amount } => {
let amount_bytes = amount.to_le_bytes();
// 在我们的程序中调用自定义转移 hook 指令
__private::__global::transfer_hook(program_id, accounts, &amount_bytes)
}
_ => return Err(ProgramError::InvalidInstructionData.into()),
}
}
Transfer hooks 是添加自定义逻辑到你代币转移中的一种强大方式。它们允许你强制业务逻辑,添加附加功能,并创建更安全、更强大的代币程序。如果你希望在代币转移中添加自定义逻辑,transfer hooks 是一个很好的起点。在我们的下一个指南中,我们将创建一个示例 transfer hook 程序,以演示它们在实践中的工作原理。敬请关注!
如果你有任何问题,请随时通过 Discord 使用我们的专用频道,或使用下面的表单提供反馈。通过关注我们的 Twitter 和我们的 Telegram 公告频道,以跟上最新动态。
让我们知道 如果你有任何反馈或新的主题请求。我们期待你的来信。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!