LogoAnchor 中文文档

扩展

了解如何在 Anchor 程序中使用 Token Extensions Program (Token 2022) 启用代币扩展,为代币铸币和账户添加可选功能。

什么是 Token Extensions?

Token Extensions Program (Token 2022) 通过称为扩展的附加指令提供了额外的功能。扩展是可以添加到代币铸币或代币账户的可选功能。你可以在 Token Extensions Program 的 源代码 中找到这些扩展指令的实现。

每个扩展都会添加特定的状态,这些状态必须在铸币或代币账户创建时初始化。在初始化任一类型的账户时,你可以同时启用多个扩展以添加不同的功能。然而,扩展不能在账户创建后添加——你必须在初始账户创建时包含所有所需的扩展。这是设计代币时的一个重要考虑因素,因为你需要提前规划希望代币支持的功能。

某些扩展彼此不兼容,不能在同一个代币铸币或代币账户上同时启用。例如,你不能将 NonTransferable 扩展与 TransferFeeConfig 扩展结合使用,因为它们的操作行为是冲突的。

Token Extensions Program 定义了一个 ExtensionType 枚举,指定了可以添加到代币铸币或代币账户的所有可用扩展。每个枚举变体代表一个具有独特功能的不同扩展。

ExtensionType 枚举定义如下:

Token Extensions
/// 可以应用于铸币或账户的扩展。铸币扩展只能应用于铸币账户,账户扩展只能应用于代币持有账户。
#[repr(u16)]
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
pub enum ExtensionType {
    /// 如果账户大小恰好为 355 时用于填充,与多重签名相同
    Uninitialized,
    /// 包括转账费率信息和伴随的提款和设置费率的权限
    TransferFeeConfig,
    /// 包括被扣留的转账费用
    TransferFeeAmount,
    /// 包括一个可选的铸币关闭权限
    MintCloseAuthority,
    /// 加密转账的审计员配置
    ConfidentialTransferMint,
    /// 加密转账的状态
    ConfidentialTransferAccount,
    /// 指定新账户的默认 Account::state
    DefaultAccountState,
    /// 表示账户所有者权限无法更改
    ImmutableOwner,
    /// 要求入账转账必须附带备忘录
    MemoTransfer,
    /// 表示该铸币的代币不可转账
    NonTransferable,
    /// 代币随时间累积利息,
    InterestBearingConfig,
    /// 通过 CPI 锁定特权代币操作
    CpiGuard,
    /// 包括一个可选的永久委托者
    PermanentDelegate,
    /// 表示该账户中的代币属于不可转账的铸币
    NonTransferableAccount,
    /// 铸币要求 CPI 调用一个实现 "transfer hook" 接口的程序
    TransferHook,
    /// 表示该账户中的代币属于具有转移钩子的铸币
    TransferHookAccount,
    /// 包括加密的被扣留费用及其加密的公钥
    ConfidentialTransferFeeConfig,
    /// 包括加密的被扣留转账费用
    ConfidentialTransferFeeAmount,
    /// 铸币包含指向另一个账户(或同一账户)的指针,该账户持有元数据
    MetadataPointer,
    /// 铸币包含代币元数据
    TokenMetadata,
    /// 铸币包含指向另一个账户(或同一账户)的指针,该账户持有组配置
    GroupPointer,
    /// 铸币包含代币组配置
    TokenGroup,
    /// 铸币包含指向另一个账户(或同一账户)的指针,该账户持有组成员配置
    GroupMemberPointer,
    /// 铸币包含代币组成员配置
    TokenGroupMember,
    /// 允许铸造和销毁加密代币的铸币
    ConfidentialMintBurn,
    /// UI 数量按给定比例缩放的代币
    ScaledUiAmount,
    /// 可以暂停铸造 / 销毁 / 转账的代币
    Pausable,
    /// 表示该账户属于可暂停的铸币
    PausableAccount,
 
    /// 测试可变长度铸币扩展
    #[cfg(test)]
    VariableLenMintTest = u16::MAX - 2,
    /// 用于使账户大小恰好为 Multisig::LEN 的填充扩展,用于测试
    #[cfg(test)]
    AccountPaddingTest,
    /// 用于使铸币大小恰好为 Multisig::LEN 的填充扩展,用于测试
    #[cfg(test)]
    MintPaddingTest,
}

每个扩展都会通过包含必须在创建铸币或代币账户时初始化的附加状态来增加专门的功能。所有扩展特定的状态都存储在 tlv_data 字段中,该字段紧随基本账户数据类型之后。tlv_data(包含扩展状态)必须根据为该账户启用的特定扩展类型进一步反序列化。

Token Extensions
/// 封装了包含可能扩展的不可变基本状态数据(铸币或账户),其中基本状态是 Pod 以进行零拷贝序列化。
#[derive(Debug, PartialEq)]
pub struct PodStateWithExtensions<'data, S: BaseState + Pod> {
    /// 解包的基本数据
    pub base: &'data S,
    /// 包含所有 TLV 数据的片段,按需反序列化


    tlv_data: &'data [u8],
}

示例

anchor-spl crate 提供了一个 token_2022_extensions 模块,其中包含用于处理代币扩展指令的辅助函数和类型。

你可以在 程序示例仓库 中找到有关如何在 Anchor 程序中使用 Token Extensions 的示例。

请注意,虽然 anchor-spl crate 提供了用于处理 Token Extensions 的辅助函数,但并非所有扩展指令都已完全实现。你可能需要为某些扩展指令手动实现 CPI 调用。

On this page

在GitHub上编辑