Luvallinen poltto

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- ja BurnChecked-ohjeet epäonnistuvat virhekoodilla TokenError::InvalidInstruction.
  • Poltoissa on käytettävä PermissionedBurnInstruction::Burn- tai PermissionedBurnInstruction::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ä.

Paikallinen testivalidator sisältää myös vanhemman Token-2022-koontiversio, joten tällä sivulla olevien esimerkkien suorittamiseksi lataa v11.0.0-ohjelma devnetistä testivalidatoriisi:

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

Kuinka luoda mint ja polttaa luvalla

Luodaksesi mintin luvallisella poltolla ja polttaaksesi tokeneita:

  1. Laske mint account -koko ja tarvittava rent mintille sekä PermissionedBurnConfig-laajennukselle.
  2. Luo mint account CreateAccount-komennolla, alusta PermissionedBurnConfig ja alusta mint InitializeMint-komennolla.
  3. Luo token account ja mintaa tokeneita.
  4. Polta PermissionedBurnInstruction::BurnChecked-komennolla, jonka allekirjoittavat sekä token account -omistaja että polttovaltuutettu.
  5. Poista luvallinen poltto valinnaisesti käytöstä SetAuthority-komennolla käyttäen AuthorityType::PermissionedBurn-komentoa ja None-valtuutusta, mikä palauttaa käyttöön tavalliset polttokäskyt. Alla olevat täydelliset koodiesimerkit näyttävät tämän vaiheen.

Laske tilin koko

Laske mint account -koko perusmintille sekä PermissionedBurnConfig-laajennukselle. Tätä kokoa käytetään CreateAccount-komennossa.

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]));

Laske rent

Laske rent käyttäen mintille sekä PermissionedBurnConfig-laajennukselle tarvittavaa kokoa.

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();

Luo mint account

Luo mint account lasketulla tilalla ja lamport-määrällä.

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
})
]);

Alusta PermissionedBurn

Alusta PermissionedBurnConfig-laajennus mintissä polttovaltuutetulla.

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
})
]);

Alusta mint

Alusta mint InitializeMint-komennolla samassa transaktiossa.

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
})
]);

Luo token account ja lyö tokeneita liikkeelle

Luo token account maksajalle ja lyö sille tokeneita liikkeelle.

Polta polttovaltuudella

Polta tokeneita PermissionedBurnInstruction::BurnChecked-komennolla, jonka allekirjoittavat sekä associated token account -omistaja että polttovaltuus.

Laske tilin koko

Laske mint account -koko perusmintille sekä PermissionedBurnConfig-laajennukselle. Tätä kokoa käytetään CreateAccount-komennossa.

Laske rent

Laske rent käyttäen mintille sekä PermissionedBurnConfig-laajennukselle tarvittavaa kokoa.

Luo mint account

Luo mint account lasketulla tilalla ja lamport-määrällä.

Alusta PermissionedBurn

Alusta PermissionedBurnConfig-laajennus mintissä polttovaltuutetulla.

Alusta mint

Alusta mint InitializeMint-komennolla samassa transaktiossa.

Luo token account ja lyö tokeneita liikkeelle

Luo token account maksajalle ja lyö sille tokeneita liikkeelle.

Polta polttovaltuudella

Polta tokeneita PermissionedBurnInstruction::BurnChecked-komennolla, jonka allekirjoittavat sekä associated token account -omistaja että polttovaltuus.

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]));

Ohjeiden järjestys

PermissionedBurnInstruction::Initialize täytyy tulla ennen InitializeMint-komentoa. CreateAccount, PermissionedBurnInstruction::Initialize ja InitializeMint täytyy sisällyttää samaan transaktioon.

Lähdeviite

KohdeKuvausLähde
PermissionedBurnConfigMint-laajennus, joka tallentaa valtuuden, jota tarvitaan jokaisen polton yhteissignointiin mintille.Lähde
PermissionedBurnInstruction::InitializeOhje, joka alustaa luvallisen polton asetukset ennen InitializeMint-komentoa.Lähde
PermissionedBurnInstruction::BurnPolttoohje, joka vaatii allekirjoitukset polttovaltuudelta sekä token account -omistajalta tai -valtuutetulta.Lähde
PermissionedBurnInstruction::BurnCheckedPolttoohje desimaalitarkistuksella, joka käyttää samoja tilejä kuin PermissionedBurnInstruction::Burn.Lähde
AuthorityType::PermissionedBurnValtuuden erottelija, jota käytetään SetAuthority-komennon kanssa polttovaltuuden kiertämiseen tai poistamiseen käytöstä mintillä.Lähde
process_initializeProsessorlogiikka, joka alustaa PermissionedBurnConfig alustamattomalle mintille ja tallentaa polttovaltuuden.Lähde
process_burnProsessorlogiikka, joka hylkää tavallisen polton, kun polttovaltuus on asetettu, ja tarkistaa valtuuden allekirjoituksen.Lähde
process_set_authorityProsessorlogiikka, joka validoi ja kiertää polttovaltuuden, kun AuthorityType::PermissionedBurn on käytössä.Lähde

TypeScript

Alla oleva Kit esimerkki käyttää generoituja ohjeita suoraan. Julkaistu perinteinen @solana/spl-token paketti ei vielä tue käyttöoikeusrajoitettua polttamista, joten perinteistä esimerkkiä ei ole sisällytetty.

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?

Sisällysluettelo

Muokkaa sivua
© 2026 Solana Foundation. Kaikki oikeudet pidätetään.