Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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