Mitä on luvallinen poltto?
Token Extensions Program -ohjelman PermissionedBurnConfig mint-laajennus
edellyttää, että määritetty polttoviranomainen allekirjoittaa yhteisesti
jokaisen mintin polton.
Kun polttoviranomainen on asetettu:
- Vakio
Burn- jaBurnChecked-ohjeet epäonnistuvat virhekoodillaTokenError::InvalidInstruction. - Poltoissa on käytettävä
PermissionedBurnInstruction::Burn- taiPermissionedBurnInstruction::BurnChecked-ohjetta, jonka allekirjoittavat sekä polttoviranomainen että token account -tilin omistaja tai valtuutettu.
Polttoviranomainen on yhteisallekirjoittaja, ei omistajan korvaaja. Viranomainen yksin ei voi polttaa tokeneita toisen token account -tililtä, eikä token account -tilin omistaja voi polttaa ilman viranomaisen allekirjoitusta. Pysyvän valtuutetun on myös käytettävä luvallisen polton ohjeita ja tarvitsee silti polttoviranomaisen yhteisallekirjoituksen.
Tämä mahdollistaa sen, että liikkeeseenlaskija voi pitää tokeneiden kokonaismäärän synkronoituna ketjun ulkopuolisen tietueen kanssa – esimerkiksi tokenisoidun omaisuuden, jonka on pysyttävä 1:1-tuettuna – estämällä haltijoita polttamasta tokeneita yksipuolisesti.
Polttoviranomaista voidaan myöhemmin vaihtaa komennolla SetAuthority
käyttämällä AuthorityType::PermissionedBurn. Viranomaisen asettaminen
arvoon None poistaa luvallisen polton käytöstä ja ottaa vakiopoltto-ohjeet
uudelleen käyttöön. Laajennuksen data pysyy kuitenkin mintissä.
Saatavuus
Luvallinen poltto on saatavilla Token-2022-ohjelman
program@v11.0.0-versiossa.
Se on tänään saatavilla devnetissä ja sen on suunniteltu otettavan käyttöön
mainnetissä kesäkuussa 2026. Token Extension Program on otettu käyttöön
erikseen kullekin klusterille, joten varmista, että käyttämäsi klusterin
käyttöönotto sisältää version v11.0.0 tai uudemman.
Paikallinen testi-validator sisältää myös vanhemman Token-2022-version, joten tällä sivulla olevien esimerkkien suorittamiseksi lataa v11.0.0-ohjelma devnetistä testi-validaattoriisi:
solana program dump -u devnet TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb token_2022.sosolana-test-validator --reset --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb token_2022.so
Kuinka luoda lyönti ja polttaa luvalla
Luvallisen polton sisältävän lyönnin luomiseen ja tokenien polttamiseen:
- Laske mint account -koko ja rent, joita tarvitaan lyönnille ja
PermissionedBurnConfig-laajennukselle. - Luo mint account
CreateAccount:lla, alustaPermissionedBurnConfigja alusta lyöntiInitializeMint:lla. - Luo token account ja lyö tokeneita.
- Polta
PermissionedBurnInstruction::BurnChecked:lla, jonka allekirjoittavat sekä token account -omistaja että polttovaltuutettu. - Poista luvallinen polttaminen halutessasi käytöstä
SetAuthority:lla käyttämälläAuthorityType::PermissionedBurn:aa jaNone-valtuutusta, mikä palauttaa käyttöön vakiopolttoinstruktiot. Alla olevat täydelliset koodiesimerkit näyttävät tämän vaiheen.
Laske tilin koko
Laske mint account -koko peruslyönnille sekä
PermissionedBurnConfig-laajennukselle. Tätä kokoa käytetään
CreateAccount:ssa.
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]));
Laske rent
Laske rent käyttäen lyönnin ja PermissionedBurnConfig-laajennuksen
tarvitsemaa kokoa.
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();
Luo mint account
Luo mint account lasketulla tilalla ja lamport-määrällä.
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})]);
Alusta PermissionedBurn
Alusta PermissionedBurnConfig-laajennus lyönnissä polttovaltuutetun
avulla.
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})]);
Alusta lyönti
Alusta lyönti InitializeMint:lla samassa transaktiossa.
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})]);
Luo token account ja lyö tokeneja
Luo payer-käyttäjälle token account ja lyö siihen tokeneja.
Polta polttovaltuudella
Polta tokeneja PermissionedBurnInstruction::BurnChecked-komennolla, jonka
allekirjoittavat sekä token account -omistaja että polttovaltuus.
Ohjeiden järjestys
PermissionedBurnInstruction::Initialize täytyy olla ennen
InitializeMint-komentoa. CreateAccount,
PermissionedBurnInstruction::Initialize ja InitializeMint täytyy
sisältyä samaan transaktioon.
Lähdeviite
| Kohde | Kuvaus | Lähde |
|---|---|---|
PermissionedBurnConfig | Mint-laajennus, joka tallentaa valtuuden, jota tarvitaan jokaisen polton yhteisallekirjoitukseen mintille. | Lähde |
PermissionedBurnInstruction::Initialize | Ohje, joka alustaa luvallisen polton asetukset ennen InitializeMint-komentoa. | Lähde |
PermissionedBurnInstruction::Burn | Poltto-ohje, joka vaatii allekirjoitukset polttovaltuudelta sekä token account -omistajalta tai valtuutetulta. | Lähde |
PermissionedBurnInstruction::BurnChecked | Poltto-ohje desimaalitarkistuksella, joka käyttää samoja tilejä kuin PermissionedBurnInstruction::Burn. | Lähde |
AuthorityType::PermissionedBurn | Valtuuden erottelija, jota käytetään SetAuthority-komennon kanssa polttovaltuuden vaihtamiseen tai poistamiseen käytöstä mintissä. | Lähde |
process_initialize | Prosessorilogiikka, joka alustaa PermissionedBurnConfig:n alustamattomaan minttiin ja tallentaa polttovaltuuden. | Lähde |
process_burn | Prosessorilogiikka, joka hylkää tavalliset poltot polttovaltuuden ollessa asetettu ja tarkistaa valtuuden allekirjoituksen. | Lähde |
process_set_authority | Prosessorilogiikka, joka vahvistaa ja vaihtaa polttovaltuuden, kun AuthorityType::PermissionedBurn:ää käytetään. | Lähde |
Typescript
Alla oleva Kit esimerkki käyttää generoituja ohjeita suoraan. Julkaistu
perinteinen @solana/spl-token paketti ei vielä tue käyttöoikeudellista
polttoa, joten perinteistä esimerkkiä ei ole sisällytetty.
Kit
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
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?