Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
905f41e
Permissioned Burn Extension
Szegoo Oct 31, 2025
f0175dc
compiles
Szegoo Oct 31, 2025
ea637bd
handle permissioned burn in processor
Szegoo Oct 31, 2025
d918f27
cleanup
Szegoo Oct 31, 2025
6a2e5bd
typo
Szegoo Oct 31, 2025
21b2e65
fmt
Szegoo Oct 31, 2025
e5ca5d4
remove unnecessary PermissionedBurnAccount
Szegoo Nov 1, 2025
d533794
Update interface/src/extension/mod.rs
Szegoo Nov 4, 2025
c22a5fe
add missing unpack & change index to 46
Szegoo Nov 4, 2025
f9133d3
implement authority type
Szegoo Nov 5, 2025
52931da
new PermissionedBurn instruction
Szegoo Nov 6, 2025
8a04278
small fix & add to rust-legacy
Szegoo Nov 8, 2025
05ee8e0
remove
Szegoo Nov 8, 2025
88b3d0b
move instruction under PermissionedBurnExtension
Szegoo Nov 26, 2025
32baa70
refactor
Szegoo Nov 26, 2025
c062644
rust-legacy test
Szegoo Nov 27, 2025
1cd3ce6
clean up & fix
Szegoo Nov 28, 2025
56e1a93
more cleanup
Szegoo Nov 28, 2025
d51e9dd
js-legacy test
Szegoo Nov 28, 2025
1f8cba9
add to cli
Szegoo Nov 28, 2025
4d7f325
add to js-client
Szegoo Nov 29, 2025
0aa93da
change order & separate enums
Szegoo Dec 5, 2025
7d9cea1
leftover ordering update & authority update test
Szegoo Dec 5, 2025
1adec9d
--permissioned-burn-authority
Szegoo Dec 5, 2025
ccc0238
handle None authority
Szegoo Dec 10, 2025
a6e910b
test None
Szegoo Dec 10, 2025
7970ee9
format & make clippy happy
Szegoo Dec 10, 2025
81b5af7
generate clients
Szegoo Dec 10, 2025
0df5cd2
Merge branch 'main' into permissioned-burn
Szegoo Dec 17, 2025
fa8911b
Update clients/cli/src/clap_app.rs
Szegoo Dec 17, 2025
789a206
fixes
Szegoo Dec 17, 2025
b36ebfc
fixes
Szegoo Dec 21, 2025
96414d2
fix nits
Szegoo Jan 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clients/cli/src/clap_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ pub enum CliAuthorityType {
Group,
ScaledUiAmount,
Pause,
PermissionedBurn,
}
impl TryFrom<CliAuthorityType> for AuthorityType {
type Error = Error;
Expand Down Expand Up @@ -277,6 +278,7 @@ impl TryFrom<CliAuthorityType> for AuthorityType {
}
CliAuthorityType::ScaledUiAmount => Ok(AuthorityType::ScaledUiAmount),
CliAuthorityType::Pause => Ok(AuthorityType::Pause),
CliAuthorityType::PermissionedBurn => Ok(AuthorityType::PermissionedBurn),
}
}
}
Expand Down Expand Up @@ -925,6 +927,21 @@ pub fn app<'a>(
"Enable the mint authority to pause mint, burn, and transfer for this mint"
)
)
.arg(
Arg::with_name("enable_permissioned_burn")
.long("enable-permissioned-burn")
.takes_value(false)
.help("Require the configured permissioned burn authority for burning tokens")
)
.arg(
Arg::with_name("permissioned_burn_authority")
.long("permissioned-burn-authority")
.validator(|s| is_valid_signer(s))
.value_name("SIGNER")
.takes_value(true)
.requires("enable_permissioned_burn")
.help("Specify a permissioned burn authority for the mint. Defaults to the mint authority.")
)
.arg(multisig_signer_arg())
.nonce_args(true)
.arg(memo_arg())
Expand Down
30 changes: 29 additions & 1 deletion clients/cli/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use {
mint_close_authority::MintCloseAuthority,
pausable::PausableConfig,
permanent_delegate::PermanentDelegate,
permissioned_burn::PermissionedBurnConfig,
scaled_ui_amount::ScaledUiAmountConfig,
transfer_fee::{TransferFeeAmount, TransferFeeConfig},
transfer_hook::TransferHook,
Expand Down Expand Up @@ -268,6 +269,8 @@ async fn command_create_token(
enable_transfer_hook: bool,
ui_multiplier: Option<f64>,
pausable: bool,
enable_permissioned_burn: bool,
permissioned_burn_authority: Option<Pubkey>,
bulk_signers: Vec<Arc<dyn Signer>>,
) -> CommandResult {
println_display(
Expand Down Expand Up @@ -409,6 +412,12 @@ async fn command_create_token(
extensions.push(ExtensionInitializationParams::PausableConfig { authority });
}

if enable_permissioned_burn {
extensions.push(ExtensionInitializationParams::PermissionedBurnConfig {
authority: permissioned_burn_authority.unwrap_or(authority),
});
}

let res = token
.create_mint(
&authority,
Expand Down Expand Up @@ -1124,6 +1133,16 @@ async fn command_authorize(
))
}
}
CliAuthorityType::PermissionedBurn => {
if let Ok(extension) = mint.get_extension::<PermissionedBurnConfig>() {
Ok(Option::<Pubkey>::from(extension.authority))
} else {
Err(format!(
"Mint `{}` does not support permissioned burn",
account
))
}
}
}?;

Ok((account, previous_authority))
Expand Down Expand Up @@ -1167,7 +1186,8 @@ async fn command_authorize(
| CliAuthorityType::Group
| CliAuthorityType::GroupMemberPointer
| CliAuthorityType::ScaledUiAmount
| CliAuthorityType::Pause => Err(format!(
| CliAuthorityType::Pause
| CliAuthorityType::PermissionedBurn => Err(format!(
"Authority type `{auth_str}` not supported for SPL Token accounts",
)),
CliAuthorityType::Owner => {
Expand Down Expand Up @@ -3775,6 +3795,12 @@ pub async fn process_command(
});
let transfer_hook_program_id =
pubkey_of_signer(arg_matches, "transfer_hook", &mut wallet_manager).unwrap();
let permissioned_burn_authority = pubkey_of_signer(
arg_matches,
"permissioned_burn_authority",
&mut wallet_manager,
)
.unwrap();

let confidential_transfer_auto_approve = arg_matches
.value_of("enable_confidential_transfers")
Expand Down Expand Up @@ -3804,6 +3830,8 @@ pub async fn process_command(
arg_matches.is_present("enable_transfer_hook"),
ui_multiplier,
arg_matches.is_present("enable_pause"),
arg_matches.is_present("enable_permissioned_burn"),
permissioned_burn_authority,
bulk_signers,
)
.await
Expand Down
36 changes: 36 additions & 0 deletions clients/cli/tests/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use {
metadata_pointer::MetadataPointer,
non_transferable::NonTransferable,
pausable::PausableConfig,
permissioned_burn::PermissionedBurnConfig,
scaled_ui_amount::ScaledUiAmountConfig,
transfer_fee::{TransferFeeAmount, TransferFeeConfig},
transfer_hook::TransferHook,
Expand Down Expand Up @@ -148,6 +149,7 @@ async fn main() {
async_trial!(compute_budget, test_validator, payer),
async_trial!(scaled_ui_amount, test_validator, payer),
async_trial!(pause, test_validator, payer),
async_trial!(permissioned_burn, test_validator, payer),
// GC messes with every other test, so have it on its own test validator
async_trial!(gc, gc_test_validator, gc_payer),
];
Expand Down Expand Up @@ -4507,3 +4509,37 @@ async fn pause(test_validator: &TestValidator, payer: &Keypair) {
let extension = test_mint.get_extension::<PausableConfig>().unwrap();
assert_eq!(Option::<Pubkey>::from(extension.authority), None,);
}

async fn permissioned_burn(test_validator: &TestValidator, payer: &Keypair) {
let config =
test_config_with_default_signer(test_validator, payer, &spl_token_2022_interface::id());

let token = Keypair::new();
let burn_authority = Keypair::new();
let token_keypair_file = NamedTempFile::new().unwrap();
write_keypair_file(&token, &token_keypair_file).unwrap();
let token_pubkey = token.pubkey();

process_test_command(
&config,
payer,
&[
"spl-token",
CommandName::CreateToken.into(),
token_keypair_file.path().to_str().unwrap(),
"--enable-permissioned-burn",
"--permissioned-burn-authority",
burn_authority.pubkey().to_string().as_str(),
],
)
.await
.unwrap();

let account = config.rpc_client.get_account(&token_pubkey).await.unwrap();
let test_mint = StateWithExtensionsOwned::<Mint>::unpack(account.data).unwrap();
let extension = test_mint.get_extension::<PermissionedBurnConfig>().unwrap();
assert_eq!(
Option::<Pubkey>::from(extension.authority),
Some(burn_authority.pubkey())
);
}
7 changes: 7 additions & 0 deletions clients/js-legacy/src/extensions/extensionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { MINT_CLOSE_AUTHORITY_SIZE } from './mintCloseAuthority.js';
import { NON_TRANSFERABLE_SIZE, NON_TRANSFERABLE_ACCOUNT_SIZE } from './nonTransferable.js';
import { PAUSABLE_CONFIG_SIZE, PAUSABLE_ACCOUNT_SIZE } from './pausable/index.js';
import { PERMANENT_DELEGATE_SIZE } from './permanentDelegate.js';
import { PERMISSIONED_BURN_SIZE } from './permissionedBurn/state.js';
import { SCALED_UI_AMOUNT_CONFIG_SIZE } from './scaledUiAmount/index.js';
import { TRANSFER_FEE_AMOUNT_SIZE, TRANSFER_FEE_CONFIG_SIZE } from './transferFee/index.js';
import { TRANSFER_HOOK_ACCOUNT_SIZE, TRANSFER_HOOK_SIZE } from './transferHook/index.js';
Expand Down Expand Up @@ -53,6 +54,7 @@ export enum ExtensionType {
ScaledUiAmountConfig = 25,
PausableConfig = 26,
PausableAccount = 27,
PermissionedBurn = 28,
}

export const TYPE_SIZE = 2;
Expand Down Expand Up @@ -123,6 +125,8 @@ export function getTypeLen(e: ExtensionType): number {
return PAUSABLE_CONFIG_SIZE;
case ExtensionType.PausableAccount:
return PAUSABLE_ACCOUNT_SIZE;
case ExtensionType.PermissionedBurn:
return PERMISSIONED_BURN_SIZE;
case ExtensionType.TokenMetadata:
throw Error(`Cannot get type length for variable extension type: ${e}`);
default:
Expand All @@ -148,6 +152,7 @@ export function isMintExtension(e: ExtensionType): boolean {
case ExtensionType.TokenGroupMember:
case ExtensionType.ScaledUiAmountConfig:
case ExtensionType.PausableConfig:
case ExtensionType.PermissionedBurn:
return true;
case ExtensionType.Uninitialized:
case ExtensionType.TransferFeeAmount:
Expand Down Expand Up @@ -192,6 +197,7 @@ export function isAccountExtension(e: ExtensionType): boolean {
case ExtensionType.TokenGroupMember:
case ExtensionType.ScaledUiAmountConfig:
case ExtensionType.PausableConfig:
case ExtensionType.PermissionedBurn:
return false;
default:
throw Error(`Unknown extension type: ${e}`);
Expand Down Expand Up @@ -230,6 +236,7 @@ export function getAccountTypeOfMintType(e: ExtensionType): ExtensionType {
case ExtensionType.TokenGroupMember:
case ExtensionType.ScaledUiAmountConfig:
case ExtensionType.PausableAccount:
case ExtensionType.PermissionedBurn:
return ExtensionType.Uninitialized;
}
}
Expand Down
1 change: 1 addition & 0 deletions clients/js-legacy/src/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export * from './transferFee/index.js';
export * from './permanentDelegate.js';
export * from './transferHook/index.js';
export * from './pausable/index.js';
export * from './permissionedBurn/index.js';
2 changes: 2 additions & 0 deletions clients/js-legacy/src/extensions/permissionedBurn/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './instructions.js';
export * from './state.js';
Loading
Loading