Skip to content
Draft
13 changes: 10 additions & 3 deletions clients/rust-legacy/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ use {
self, ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig,
},
cpi_guard, default_account_state, group_member_pointer, group_pointer,
interest_bearing_mint, memo_transfer, metadata_pointer, pausable, scaled_ui_amount,
transfer_fee, transfer_hook, BaseStateWithExtensions, Extension, ExtensionType,
StateWithExtensionsOwned,
interest_bearing_mint, memo_transfer, metadata_pointer, pausable, permissioned_burn,
scaled_ui_amount, transfer_fee, transfer_hook, BaseStateWithExtensions, Extension,
ExtensionType, StateWithExtensionsOwned,
},
instruction,
solana_zk_sdk::{
Expand Down Expand Up @@ -201,6 +201,9 @@ pub enum ExtensionInitializationParams {
PausableConfig {
authority: Pubkey,
},
PermissionedBurnConfig {
authority: Pubkey,
},
ConfidentialMintBurn {
supply_elgamal_pubkey: PodElGamalPubkey,
decryptable_supply: PodAeCiphertext,
Expand All @@ -226,6 +229,7 @@ impl ExtensionInitializationParams {
Self::GroupMemberPointer { .. } => ExtensionType::GroupMemberPointer,
Self::ScaledUiAmountConfig { .. } => ExtensionType::ScaledUiAmount,
Self::PausableConfig { .. } => ExtensionType::Pausable,
Self::PermissionedBurnConfig { .. } => ExtensionType::PermissionedBurn,
Self::ConfidentialMintBurn { .. } => ExtensionType::ConfidentialMintBurn,
}
}
Expand Down Expand Up @@ -348,6 +352,9 @@ impl ExtensionInitializationParams {
Self::PausableConfig { authority } => {
pausable::instruction::initialize(token_program_id, mint, &authority)
}
Self::PermissionedBurnConfig { authority } => {
permissioned_burn::instruction::initialize(token_program_id, mint, &authority)
}
Self::ConfidentialMintBurn {
supply_elgamal_pubkey,
decryptable_supply,
Expand Down
9 changes: 8 additions & 1 deletion interface/src/extension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use {
non_transferable::{NonTransferable, NonTransferableAccount},
pausable::{PausableAccount, PausableConfig},
permanent_delegate::PermanentDelegate,
permissioned_burn::PermissionedBurnConfig,
scaled_ui_amount::ScaledUiAmountConfig,
transfer_fee::{TransferFeeAmount, TransferFeeConfig},
transfer_hook::{TransferHook, TransferHookAccount},
Expand Down Expand Up @@ -76,6 +77,8 @@ pub mod non_transferable;
pub mod pausable;
/// Permanent Delegate extension
pub mod permanent_delegate;
/// Permissioned burn extension
pub mod permissioned_burn;
/// Scaled UI Amount extension
pub mod scaled_ui_amount;
/// Token-group extension
Expand Down Expand Up @@ -1119,6 +1122,8 @@ pub enum ExtensionType {
Pausable,
/// Indicates that the account belongs to a pausable mint
PausableAccount,
/// Tokens burning requires approval from authorirty.
PermissionedBurn,

/// Test variable-length mint extension
#[cfg(test)]
Expand Down Expand Up @@ -1204,6 +1209,7 @@ impl ExtensionType {
ExtensionType::ScaledUiAmount => pod_get_packed_len::<ScaledUiAmountConfig>(),
ExtensionType::Pausable => pod_get_packed_len::<PausableConfig>(),
ExtensionType::PausableAccount => pod_get_packed_len::<PausableAccount>(),
ExtensionType::PermissionedBurn => pod_get_packed_len::<PermissionedBurnConfig>(),
#[cfg(test)]
ExtensionType::AccountPaddingTest => pod_get_packed_len::<AccountPaddingTest>(),
#[cfg(test)]
Expand Down Expand Up @@ -1270,7 +1276,8 @@ impl ExtensionType {
| ExtensionType::ConfidentialMintBurn
| ExtensionType::TokenGroupMember
| ExtensionType::ScaledUiAmount
| ExtensionType::Pausable => AccountType::Mint,
| ExtensionType::Pausable
| ExtensionType::PermissionedBurn => AccountType::Mint,
ExtensionType::ImmutableOwner
| ExtensionType::TransferFeeAmount
| ExtensionType::ConfidentialTransferAccount
Expand Down
59 changes: 59 additions & 0 deletions interface/src/extension/permissioned_burn/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use {
crate::{
check_program_account,
instruction::{encode_instruction, TokenInstruction},
},
bytemuck::{Pod, Zeroable},
num_enum::{IntoPrimitive, TryFromPrimitive},
solana_instruction::{AccountMeta, Instruction},
solana_program_error::ProgramError,
solana_pubkey::Pubkey,
};

/// Permissioned Burn extension instructions
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(u8)]
pub enum PermissionedBurnInstruction {
/// Require permissioned burn for the given mint account
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The mint account to initialize.
///
/// Data expected by this instruction:
/// `crate::extension::permissioned_burn::instruction::InitializeInstructionData`
Initialize,
}

/// Data expected by `PermissionedBurnInstruction::Initialize`
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct InitializeInstructionData {
/// The public key for the account that is required for token burning.
pub authority: Pubkey,
}

/// Create an `Initialize` instruction
pub fn initialize(
token_program_id: &Pubkey,
mint: &Pubkey,
authority: &Pubkey,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![AccountMeta::new(*mint, false)];
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::PermissionedBurnExtension,
PermissionedBurnInstruction::Initialize,
&InitializeInstructionData {
authority: *authority,
},
))
}
24 changes: 24 additions & 0 deletions interface/src/extension/permissioned_burn/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use {
crate::extension::{Extension, ExtensionType},
bytemuck::{Pod, Zeroable},
spl_pod::optional_keys::OptionalNonZeroPubkey,
};

/// Instruction types for the permissioned burn extension
pub mod instruction;

/// Indicates that the tokens from this mint require permissioned burn
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
#[repr(C)]
pub struct PermissionedBurnConfig {
/// Authority that is required for burning
pub authority: OptionalNonZeroPubkey,
}

impl Extension for PermissionedBurnConfig {
const TYPE: ExtensionType = ExtensionType::PermissionedBurn;
}
10 changes: 10 additions & 0 deletions interface/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,8 @@ pub enum TokenInstruction<'a> {
ScaledUiAmountExtension,
/// Instruction prefix for instructions to the pausable extension
PausableExtension,
/// Instruction prefix for instructions to the permissioned burn extension
PermissionedBurnExtension,
}
impl<'a> TokenInstruction<'a> {
/// Unpacks a byte buffer into a
Expand Down Expand Up @@ -873,6 +875,7 @@ impl<'a> TokenInstruction<'a> {
42 => Self::ConfidentialMintBurnExtension,
43 => Self::ScaledUiAmountExtension,
44 => Self::PausableExtension,
46 => Self::PermissionedBurnExtension,
_ => return Err(TokenError::InvalidInstruction.into()),
})
}
Expand Down Expand Up @@ -1053,6 +1056,9 @@ impl<'a> TokenInstruction<'a> {
&Self::PausableExtension => {
buf.push(44);
}
&Self::PermissionedBurnExtension => {
buf.push(46);
}
};
buf
}
Expand Down Expand Up @@ -1154,6 +1160,8 @@ pub enum AuthorityType {
ScaledUiAmount,
/// Authority to pause or resume minting / transferring / burning
Pause,
/// Authority to perform a permissioned token burn
PermissionedBurn,
}

impl AuthorityType {
Expand All @@ -1176,6 +1184,7 @@ impl AuthorityType {
AuthorityType::GroupMemberPointer => 14,
AuthorityType::ScaledUiAmount => 15,
AuthorityType::Pause => 16,
AuthorityType::PermissionedBurn => 17,
}
}

Expand All @@ -1199,6 +1208,7 @@ impl AuthorityType {
14 => Ok(AuthorityType::GroupMemberPointer),
15 => Ok(AuthorityType::ScaledUiAmount),
16 => Ok(AuthorityType::Pause),
17 => Ok(AuthorityType::PermissionedBurn),
_ => Err(TokenError::InvalidInstruction.into()),
}
}
Expand Down
2 changes: 2 additions & 0 deletions program/src/extension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub mod non_transferable;
pub mod pausable;
/// Permanent Delegate extension
pub mod permanent_delegate;
/// Permissioned burn extension
pub mod permissioned_burn;
/// Utility to reallocate token accounts
pub mod reallocate;
/// Scaled UI Amount extension
Expand Down
5 changes: 5 additions & 0 deletions program/src/extension/permissioned_burn/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![deprecated(
since = "9.1.0",
note = "Use spl_token_2022_interface instead and remove spl_token_2022 as a dependency"
)]
pub use spl_token_2022_interface::extension::permissioned_burn::instruction::*;
10 changes: 10 additions & 0 deletions program/src/extension/permissioned_burn/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// Instruction types for the permissioned burn extension
pub mod instruction;
/// Instruction processor for the permissioned burn extension
pub mod processor;

#[deprecated(
since = "9.1.0",
note = "Use spl_token_2022_interface instead and remove spl_token_2022 as a dependency"
)]
pub use spl_token_2022_interface::extension::permissioned_burn::PermissionedBurnConfig;
50 changes: 50 additions & 0 deletions program/src/extension/permissioned_burn/processor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use {
solana_account_info::{next_account_info, AccountInfo},
solana_msg::msg,
solana_program_error::ProgramResult,
solana_pubkey::Pubkey,
spl_token_2022_interface::{
check_program_account,
extension::{
permissioned_burn::{
instruction::{InitializeInstructionData, PermissionedBurnInstruction},
PermissionedBurnConfig,
},
BaseStateWithExtensionsMut, PodStateWithExtensionsMut,
},
instruction::{decode_instruction_data, decode_instruction_type},
pod::PodMint,
},
};

fn process_initialize(
_program_id: &Pubkey,
accounts: &[AccountInfo],
authority: &Pubkey,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let mint_account_info = next_account_info(account_info_iter)?;
let mut mint_data = mint_account_info.data.borrow_mut();
let mut mint = PodStateWithExtensionsMut::<PodMint>::unpack_uninitialized(&mut mint_data)?;

let extension = mint.init_extension::<PermissionedBurnConfig>(true)?;
extension.authority = Some(*authority).try_into()?;

Ok(())
}

pub(crate) fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
input: &[u8],
) -> ProgramResult {
check_program_account(program_id)?;

match decode_instruction_type(input)? {
PermissionedBurnInstruction::Initialize => {
msg!("PermissionedBurnInstruction::Initialize");
let InitializeInstructionData { authority } = decode_instruction_data(input)?;
process_initialize(program_id, accounts, authority)
}
}
}
3 changes: 3 additions & 0 deletions program/src/pod_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ pub(crate) enum PodTokenInstruction {
ConfidentialMintBurnExtension,
ScaledUiAmountExtension,
PausableExtension,
// 45
PermissionedBurnExtension,
PermissionedBurn,
}

fn unpack_pubkey_option(input: &[u8]) -> Result<PodCOption<Pubkey>, ProgramError> {
Expand Down
Loading