권한 있는 소각

권한 있는 소각이란?

Token Extensions Program의 PermissionedBurnConfig 민트 확장은 해당 민트의 모든 소각에 설정된 소각 권한자의 공동 서명을 요구합니다.

소각 권한자가 설정되어 있는 동안:

  • 표준 BurnBurnChecked 명령어는 TokenError::InvalidInstruction 오류와 함께 실패합니다.
  • 소각 시 PermissionedBurnInstruction::Burn 또는 *rsPermissionedBurnInstruction::BurnChecked*을 사용해야 하며, 소각 권한자와 token account의 소유자 또는 위임자 모두의 서명이 필요합니다.

소각 권한자는 소유자를 대체하는 것이 아니라 공동 서명자입니다. 권한자 단독으로는 타인의 token account에서 토큰을 소각할 수 없으며, token account 소유자도 권한자의 서명 없이는 소각할 수 없습니다. 영구 위임자도 권한 있는 소각 명령어를 사용해야 하며, 소각 권한자의 공동 서명이 여전히 필요합니다.

이를 통해 발행자는 토큰 공급량을 오프체인 기록과 동기화할 수 있습니다. 예를 들어, 1:1 담보를 유지해야 하는 토큰화 자산의 경우 보유자가 일방적으로 토큰을 소각하는 것을 방지할 수 있습니다.

소각 권한자는 나중에 *rsAuthorityType::PermissionedBurn*를 사용하여 *rsSetAuthority*로 교체할 수 있습니다. 권한자를 None로 설정하면 권한 있는 소각이 비활성화되고 표준 소각 명령어가 다시 활성화됩니다. 확장 데이터 자체는 민트에 그대로 유지됩니다.

사용 가능 여부

권한 있는 소각은 Token-2022 program@v11.0.0에 포함되어 출시됩니다. 현재 devnet에서 사용 가능하며, 2026년 6월 메인넷 활성화가 예정되어 있습니다. Token Extension Program은 각 클러스터에 별도로 배포되므로, 대상 클러스터의 배포 버전이 v11.0.0 이상인지 확인하세요.

로컬 테스트 validator에는 이전 버전의 Token-2022 빌드가 포함되어 있으므로, 이 페이지의 예제를 실행하려면 devnet에서 v11.0.0 프로그램을 테스트 validator에 로드하세요:

Terminal
solana program dump -u devnet TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb token_2022.so
solana-test-validator --reset --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb token_2022.so

권한 있는 소각으로 Mint 생성 및 소각하는 방법

권한 있는 소각으로 mint를 생성하고 토큰을 소각하려면:

  1. mint account 크기와 mint 및 PermissionedBurnConfig 확장에 필요한 rent를 계산합니다.
  2. *rsCreateAccount*로 mint account를 생성하고, *rsPermissionedBurnConfig*를 초기화한 후, *rsInitializeMint*로 mint를 초기화합니다.
  3. token account를 생성하고 토큰을 발행합니다.
  4. token account 소유자와 소각 권한자가 모두 서명한 *rsPermissionedBurnInstruction::BurnChecked*로 소각합니다.
  5. 선택적으로 *rsAuthorityType::PermissionedBurn*와 None 권한자를 사용하여 *rsSetAuthority*로 권한 있는 소각을 비활성화하면 표준 소각 명령어가 다시 활성화됩니다. 아래의 전체 코드 예제에서 이 단계를 확인할 수 있습니다.

계정 크기 계산

기본 mint와 PermissionedBurnConfig 확장을 위한 mint account 크기를 계산합니다. 이 크기는 *rsCreateAccount*에서 사용됩니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));

rent 계산

mint와 PermissionedBurnConfig 확장에 필요한 크기를 사용하여 rent를 계산합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();

mint account 생성

계산된 공간과 lamport로 mint account를 생성합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
})
]);

PermissionedBurn 초기화

소각 권한자로 mint에 PermissionedBurnConfig 확장을 초기화합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializePermissionedBurnInstruction({
mint: mint.address,
authority: burnAuthority.address
})
]);

mint 초기화

동일한 트랜잭션에서 *rsInitializeMint*로 mint를 초기화합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializePermissionedBurnInstruction({
mint: mint.address,
authority: burnAuthority.address
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);

token account 생성 및 토큰 발행

지불자를 위한 token account를 생성하고 토큰을 발행합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializePermissionedBurnInstruction({
mint: mint.address,
authority: burnAuthority.address
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const [sourceToken] = await findAssociatedTokenPda({
mint: mint.address,
owner: client.payer.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
await client.sendTransaction([
await getCreateAssociatedTokenInstructionAsync({
payer: client.payer,
mint: mint.address,
owner: client.payer.address
}),
getMintToCheckedInstruction({
mint: mint.address,
token: sourceToken,
mintAuthority: client.payer,
amount: 2n,
decimals: 0
})
]);

소각 권한으로 소각

token account 소유자와 소각 권한자 모두가 서명한 *rsPermissionedBurnInstruction::BurnChecked*로 토큰을 소각합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer,
newAccount: mint,
lamports: mintRent,
space: mintSpace,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
}),
getInitializePermissionedBurnInstruction({
mint: mint.address,
authority: burnAuthority.address
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const [sourceToken] = await findAssociatedTokenPda({
mint: mint.address,
owner: client.payer.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
await client.sendTransaction([
await getCreateAssociatedTokenInstructionAsync({
payer: client.payer,
mint: mint.address,
owner: client.payer.address
}),
getMintToCheckedInstruction({
mint: mint.address,
token: sourceToken,
mintAuthority: client.payer,
amount: 2n,
decimals: 0
})
]);
await client.sendTransaction([
getPermissionedBurnCheckedInstruction({
account: sourceToken,
mint: mint.address,
permissionedBurnAuthority: burnAuthority,
authority: client.payer,
amount: 1n,
decimals: 0
})
]);

계정 크기 계산

기본 mint와 PermissionedBurnConfig 확장을 위한 mint account 크기를 계산합니다. 이 크기는 *rsCreateAccount*에서 사용됩니다.

rent 계산

mint와 PermissionedBurnConfig 확장에 필요한 크기를 사용하여 rent를 계산합니다.

mint account 생성

계산된 공간과 lamport로 mint account를 생성합니다.

PermissionedBurn 초기화

소각 권한자로 mint에 PermissionedBurnConfig 확장을 초기화합니다.

mint 초기화

동일한 트랜잭션에서 *rsInitializeMint*로 mint를 초기화합니다.

token account 생성 및 토큰 발행

지불자를 위한 token account를 생성하고 토큰을 발행합니다.

소각 권한으로 소각

token account 소유자와 소각 권한자 모두가 서명한 *rsPermissionedBurnInstruction::BurnChecked*로 토큰을 소각합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));

명령어 순서

_rsPermissionedBurnInstruction::Initialize_은 InitializeMint 이전에 와야 합니다. CreateAccount, PermissionedBurnInstruction::Initialize, _rsInitializeMint_은 동일한 트랜잭션에 포함되어야 합니다.

소스 참조

항목설명소스
PermissionedBurnConfig해당 민트의 모든 소각에 공동 서명하는 데 필요한 권한을 저장하는 민트 확장입니다.소스
PermissionedBurnInstruction::InitializeInitializeMint 이전에 허가된 소각 설정을 초기화하는 명령어입니다.소스
PermissionedBurnInstruction::Burn소각 권한자와 token account 소유자 또는 위임자의 서명을 요구하는 소각 명령어입니다.소스
PermissionedBurnInstruction::BurnChecked*rsPermissionedBurnInstruction::Burn*과 동일한 계정을 사용하는 소수점 확인이 포함된 소각 명령어입니다.소스
AuthorityType::PermissionedBurn민트의 소각 권한을 교체하거나 비활성화하기 위해 *rsSetAuthority*와 함께 사용되는 권한 식별자입니다.소스
process_initialize초기화되지 않은 민트에 *rsPermissionedBurnConfig*를 초기화하고 소각 권한을 저장하는 프로세서 로직입니다.소스
process_burn소각 권한이 설정된 경우 표준 소각을 거부하고 권한의 서명을 검증하는 프로세서 로직입니다.소스
process_set_authorityAuthorityType::PermissionedBurn 사용 시 소각 권한을 검증하고 교체하는 프로세서 로직입니다.소스

Typescript

아래의 Kit 예제는 생성된 명령어를 직접 사용합니다. 게시된 레거시 @solana/spl-token 패키지는 아직 권한 있는 소각 지원을 포함하지 않으므로, 레거시 예제는 포함되지 않습니다.

Kit

Instructions
import {
lamports,
createClient,
generateKeyPairSigner,
unwrapOption
} from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
AuthorityType,
extension,
fetchMint,
fetchToken,
findAssociatedTokenPda,
getBurnCheckedInstruction,
getCreateAssociatedTokenInstructionAsync,
getInitializeMintInstruction,
getInitializePermissionedBurnInstruction,
getMintSize,
getMintToCheckedInstruction,
getPermissionedBurnCheckedInstruction,
getSetAuthorityInstruction,
isExtension,
TOKEN_2022_PROGRAM_ADDRESS
} from "@solana-program/token-2022";
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const mint = await generateKeyPairSigner();
const burnAuthority = await generateKeyPairSigner();
const permissionedBurnExtension = extension("PermissionedBurn", {
authority: burnAuthority.address
});
const mintSpace = BigInt(getMintSize([permissionedBurnExtension]));
const mintRent = await client.rpc
.getMinimumBalanceForRentExemption(mintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer, // Account funding account creation.
newAccount: mint, // New mint account to create.
lamports: mintRent, // Lamports funding the mint account rent.
space: mintSpace, // Account size in bytes for the mint plus PermissionedBurnConfig.
programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.
}),
getInitializePermissionedBurnInstruction({
mint: mint.address, // Mint account that stores the PermissionedBurnConfig extension.
authority: burnAuthority.address // Authority required to co-sign every burn for the mint.
}),
getInitializeMintInstruction({
mint: mint.address, // Mint account to initialize.
decimals: 0, // Number of decimals for the token.
mintAuthority: client.payer.address, // Authority allowed to mint new tokens.
freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.
})
]);
const [sourceToken] = await findAssociatedTokenPda({
mint: mint.address,
owner: client.payer.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
await client.sendTransaction([
await getCreateAssociatedTokenInstructionAsync({
payer: client.payer, // Account funding the associated token account creation.
mint: mint.address, // Mint for the associated token account.
owner: client.payer.address // Owner of the associated token account.
}),
getMintToCheckedInstruction({
mint: mint.address, // Mint account that issues the tokens.
token: sourceToken, // Token account receiving the newly minted tokens.
mintAuthority: client.payer, // Signer authorized to mint new tokens.
amount: 2n, // Token amount in base units.
decimals: 0 // Decimals defined on the mint.
})
]);
let standardBurnFailure: string | undefined;
try {
await client.sendTransaction([
getBurnCheckedInstruction({
account: sourceToken, // Token account to burn from.
mint: mint.address, // Mint with the permissioned burn configuration.
authority: client.payer, // Token account owner signing the burn.
amount: 1n, // Token amount in base units.
decimals: 0 // Decimals defined on the mint.
})
]);
} catch (error) {
standardBurnFailure = error instanceof Error ? error.message : String(error);
}
if (!standardBurnFailure) {
throw new Error("Expected the standard burn to fail");
}
await client.sendTransaction([
getPermissionedBurnCheckedInstruction({
account: sourceToken, // Token account to burn from.
mint: mint.address, // Mint with the permissioned burn configuration.
permissionedBurnAuthority: burnAuthority, // Burn authority co-signing the burn.
authority: client.payer, // Token account owner signing the burn.
amount: 1n, // Token amount in base units.
decimals: 0 // Decimals defined on the mint.
})
]);
await client.sendTransaction([
getSetAuthorityInstruction({
owned: mint.address, // Mint with the permissioned burn configuration.
owner: burnAuthority, // Current burn authority signing the update.
authorityType: AuthorityType.PermissionedBurn, // Authority type to update.
newAuthority: null // Setting the authority to None disables permissioned burning.
})
]);
await client.sendTransaction([
getBurnCheckedInstruction({
account: sourceToken, // Token account to burn from.
mint: mint.address, // Mint with permissioned burning disabled.
authority: client.payer, // Token account owner signing the burn.
amount: 1n, // Token amount in base units.
decimals: 0 // Decimals defined on the mint.
})
]);
const sourceAccount = await fetchToken(client.rpc, sourceToken);
const mintAccount = await fetchMint(client.rpc, mint.address);
const permissionedBurnConfig = (
unwrapOption(mintAccount.data.extensions) ?? []
).find((item) => isExtension("PermissionedBurn", item));
console.log("Mint Address:", mint.address);
console.log("Error From Failed Standard Burn:", standardBurnFailure);
console.log("Source Amount After Burns:", sourceAccount.data.amount);
console.log("Extension After Disable:", permissionedBurnConfig);

Rust

Rust
use anyhow::{anyhow, Result};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
signature::{Keypair, Signer},
transaction::Transaction,
};
use solana_system_interface::instruction::create_account;
use spl_associated_token_account_interface::{
address::get_associated_token_address_with_program_id,
instruction::create_associated_token_account,
};
use spl_token_2022_interface::{
extension::{
permissioned_burn::{instruction as permissioned_burn_ix, PermissionedBurnConfig},
BaseStateWithExtensions, ExtensionType, StateWithExtensions,
},
instruction::{burn_checked, initialize_mint, mint_to_checked, set_authority, AuthorityType},
state::{Account, Mint},
ID as TOKEN_2022_PROGRAM_ID,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let fee_payer = Keypair::new();
let burn_authority = Keypair::new();
let airdrop_signature = client
.request_airdrop(&fee_payer.pubkey(), 5_000_000_000)
.await?;
loop {
let confirmed = client.confirm_transaction(&airdrop_signature).await?;
if confirmed {
break;
}
}
let mint = Keypair::new();
let mint_space =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::PermissionedBurn])?;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space)
.await?;
let create_mint_transaction = Transaction::new_signed_with_payer(
&[
create_account(
&fee_payer.pubkey(), // Account funding account creation.
&mint.pubkey(), // New mint account to create.
mint_rent, // Lamports funding the mint account rent.
mint_space as u64, // Account size in bytes for the mint plus PermissionedBurnConfig.
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
),
permissioned_burn_ix::initialize(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
&mint.pubkey(), // Mint account that stores the PermissionedBurnConfig extension.
&burn_authority.pubkey(), // Authority required to co-sign every burn for the mint.
)?,
initialize_mint(
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
&mint.pubkey(), // Mint account to initialize.
&fee_payer.pubkey(), // Authority allowed to mint new tokens.
Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.
0, // Number of decimals for the token.
)?,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&create_mint_transaction)
.await?;
let source_token = get_associated_token_address_with_program_id(
&fee_payer.pubkey(),
&mint.pubkey(),
&TOKEN_2022_PROGRAM_ID,
);
let create_token_account_transaction = Transaction::new_signed_with_payer(
&[
create_associated_token_account(
&fee_payer.pubkey(), // Account funding the associated token account creation.
&fee_payer.pubkey(), // Owner of the associated token account.
&mint.pubkey(), // Mint for the associated token account.
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
),
mint_to_checked(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint and token account.
&mint.pubkey(), // Mint account that issues the tokens.
&source_token, // Token account receiving the newly minted tokens.
&fee_payer.pubkey(), // Signer authorized to mint new tokens.
&[&fee_payer.pubkey()], // Additional multisig signers.
2, // Token amount in base units.
0, // Decimals defined on the mint.
)?,
],
Some(&fee_payer.pubkey()),
&[&fee_payer],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&create_token_account_transaction)
.await?;
let standard_burn_ix = burn_checked(
&TOKEN_2022_PROGRAM_ID, // Token program that processes the burn.
&source_token, // Token account to burn from.
&mint.pubkey(), // Mint with the permissioned burn configuration.
&fee_payer.pubkey(), // Token account owner signing the burn.
&[&fee_payer.pubkey()], // Additional multisig signers.
1, // Token amount in base units.
0, // Decimals defined on the mint.
)?;
let standard_burn_transaction = Transaction::new_signed_with_payer(
&[standard_burn_ix],
Some(&fee_payer.pubkey()),
&[&fee_payer],
client.get_latest_blockhash().await?,
);
let standard_burn_result = client
.simulate_transaction(&standard_burn_transaction)
.await?;
let standard_burn_failure = standard_burn_result
.value
.err
.ok_or_else(|| anyhow!("Expected the standard burn to fail"))?;
let permissioned_burn_instruction = permissioned_burn_ix::burn_checked(
&TOKEN_2022_PROGRAM_ID, // Token program that processes the burn.
&source_token, // Token account to burn from.
&mint.pubkey(), // Mint with the permissioned burn configuration.
&burn_authority.pubkey(), // Burn authority co-signing the burn.
&fee_payer.pubkey(), // Token account owner signing the burn.
&[], // Additional multisig signers.
1, // Token amount in base units.
0, // Decimals defined on the mint.
)?;
let permissioned_burn_transaction = Transaction::new_signed_with_payer(
&[permissioned_burn_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer, &burn_authority],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&permissioned_burn_transaction)
.await?;
let disable_authority_transaction = Transaction::new_signed_with_payer(
&[
set_authority(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
&mint.pubkey(), // Mint with the permissioned burn configuration.
None, // Setting the authority to None disables permissioned burning.
AuthorityType::PermissionedBurn, // Authority type to update.
&burn_authority.pubkey(), // Current burn authority signing the update.
&[], // Additional multisig signers.
)?,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &burn_authority],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&disable_authority_transaction)
.await?;
let standard_burn_after_disable_transaction = Transaction::new_signed_with_payer(
&[
burn_checked(
&TOKEN_2022_PROGRAM_ID, // Token program that processes the burn.
&source_token, // Token account to burn from.
&mint.pubkey(), // Mint with permissioned burning disabled.
&fee_payer.pubkey(), // Token account owner signing the burn.
&[&fee_payer.pubkey()], // Additional multisig signers.
1, // Token amount in base units.
0, // Decimals defined on the mint.
)?,
],
Some(&fee_payer.pubkey()),
&[&fee_payer],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&standard_burn_after_disable_transaction)
.await?;
let source_account = client.get_account(&source_token).await?;
let source_state = StateWithExtensions::<Account>::unpack(&source_account.data)?;
let mint_account = client.get_account(&mint.pubkey()).await?;
let mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;
let permissioned_burn_config = mint_state.get_extension::<PermissionedBurnConfig>()?;
println!("Mint Address: {}", mint.pubkey());
println!(
"Error From Failed Standard Burn: {:?}",
standard_burn_failure
);
println!(
"Source Amount After Burns: {}",
u64::from(source_state.base.amount)
);
println!("Extension After Disable: {:?}", permissioned_burn_config);
Ok(())
}

Is this page helpful?

목차

페이지 편집