Mikä on muuttumaton omistaja?
Token Extensions Program -ohjelman ImmutableOwner -tilialustan laajennus
tekee token accountin omistajasta pysyvän.
Kun token account on alustettu ImmutableOwner -laajennuksella, myöhemmät
SetAuthority -käskyt eivät voi muuttaa sen AccountOwner
-valtuutusta.
Associated Token Accounts
Associated Token Program -ohjelman kautta luodut associated token accountit
Token Extensions Program -ohjelmalle alustavat automaattisesti
ImmutableOwner -laajennuksen token accountille.
Miten alustetaan muuttumattoman omistajan tili
Muuttumattoman omistajan tilin alustamiseksi:
- Luo ja alusta mint.
- Luo token account, jossa on riittävästi tilaa
ImmutableOwner-laajennukselle. - Alusta
ImmutableOwnertoken accountille ennenInitializeAccount-käskyä. - Yritys vaihtaa tilin omistajaa myöhemmin
SetAuthority-käskyllä epäonnistuu virheelläTokenError::ImmutableOwner.
Laske token accountin koko
Laske token accountin koko perus-token accountille lisättynä
ImmutableOwner -laajennuksella. Tätä kokoa käytetään CreateAccount
-käskyssä.
Laske vuokra
Laske vuokra käyttäen token accountin kokoa, joka tarvitaan ImmutableOwner
-laajennukselle.
Luo token account
Luo token account lasketulla tilalla ja lamporteilla.
Alusta ImmutableOwner
Alusta ImmutableOwner -laajennus token-tilille.
Alusta token-tili
Alusta token-tili InitializeAccount -komennolla samassa transaktiossa.
Käskyjen järjestys
InitializeImmutableOwner on suoritettava ennen InitializeAccount
-komentoa. CreateAccount, InitializeImmutableOwner ja
InitializeAccount on sisällytettävä samaan transaktioon.
Lähdeviite
| Kohde | Kuvaus | Lähde |
|---|---|---|
ImmutableOwner | Tilienlaajennus, joka merkitsee token-tilin omistajan muuttumattomaksi. | Lähde |
InitializeImmutableOwner | Käsky, joka alustaa muuttumaton omistaja -laajennuksen alustamattomalle token-tilille. | Lähde |
process_initialize_immutable_owner | Prosessorin logiikka, joka varaa ja alustaa ImmutableOwner -laajennuksen tilille. | Lähde |
SetAuthority | Token-peruskäsky, jota käytetään valtuuksien muuttamiseen, mukaan lukien token-tilin omistaja. | Lähde |
process_set_authority | Prosessorin logiikka, joka estää omistajan vaihdokset, kun token-tilillä on ImmutableOwner käytössä. | Lähde |
Typescript
Alla oleva Kit -esimerkki käyttää generoituja käskyjä suoraan. Vanhat
esimerkit, joissa käytetään @solana/web3.js- ja
@solana/spl-token-kirjastoja, on sisällytetty vertailua varten.
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,fetchToken,getInitializeAccountInstruction,getInitializeImmutableOwnerInstruction,getInitializeMintInstruction,getMintSize,getSetAuthorityInstruction,getTokenSize,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 tokenAccount = await generateKeyPairSigner();const newOwner = await generateKeyPairSigner();const mintSpace = BigInt(getMintSize());const mintRent = await client.rpc.getMinimumBalanceForRentExemption(mintSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding the new mint account.newAccount: mint, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintSpace, // Account size in bytes for the mint.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.}),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 immutableOwnerExtension = extension("ImmutableOwner", {});const tokenSpace = BigInt(getTokenSize([immutableOwnerExtension]));const tokenRent = await client.rpc.getMinimumBalanceForRentExemption(tokenSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding the new token account.newAccount: tokenAccount, // New token account to create.lamports: tokenRent, // Lamports funding the token account rent.space: tokenSpace, // Account size in bytes for the token account plus ImmutableOwner.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the token account.}),getInitializeImmutableOwnerInstruction({account: tokenAccount.address // Token account that stores the ImmutableOwner extension.}),getInitializeAccountInstruction({account: tokenAccount.address, // Token account to initialize.mint: mint.address, // Mint for the token account.owner: client.payer.address // Owner of the token account.})]);let failure: string | undefined;try {await client.sendTransaction([getSetAuthorityInstruction({owned: tokenAccount.address, // Token account whose authority is being updated.owner: client.payer, // Current token account owner signing the instruction.authorityType: AuthorityType.AccountOwner, // Account authority field to change.newAuthority: newOwner.address // New token account owner to set.})]);} catch (error) {failure = String(error);}if (!failure) {throw new Error("Expected the owner change to fail");}const token = await fetchToken(client.rpc, tokenAccount.address);const immutableOwnerExtensionState = (unwrapOption(token.data.extensions) ?? []).find((item) => isExtension("ImmutableOwner", item));console.log("Mint Address:", mint.address);console.log("Token Account:", tokenAccount.address);console.log("ImmutableOwner Extension:", immutableOwnerExtensionState);console.log("SetAuthority Failure:", failure);
Web3.js
import {Connection,Keypair,sendAndConfirmTransaction,SystemProgram,Transaction,LAMPORTS_PER_SOL} from "@solana/web3.js";import {AuthorityType,createInitializeMintInstruction,createInitializeAccountInstruction,createInitializeImmutableOwnerInstruction,createSetAuthorityInstruction,ExtensionType,getAccount,getAccountLen,getImmutableOwner,getMintLen,TOKEN_2022_PROGRAM_ID} from "@solana/spl-token";const connection = new Connection("http://localhost:8899", "confirmed");const feePayer = Keypair.generate();const newOwner = Keypair.generate();const airdropSignature = await connection.requestAirdrop(feePayer.publicKey,5 * LAMPORTS_PER_SOL);const latestBlockhash = await connection.getLatestBlockhash();await connection.confirmTransaction({blockhash: latestBlockhash.blockhash,lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,signature: airdropSignature});const mint = Keypair.generate();const mintLength = getMintLen([]);const mintRent = await connection.getMinimumBalanceForRentExemption(mintLength);const extensions = [ExtensionType.ImmutableOwner];const tokenAccount = new Keypair();const tokenAccountLen = getAccountLen(extensions);const tokenAccountRent =await connection.getMinimumBalanceForRentExemption(tokenAccountLen);const createMintAccountInstruction = SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding the new mint account.newAccountPubkey: mint.publicKey, // New mint account to create.space: mintLength, // Account size in bytes for the mint.lamports: mintRent, // Lamports funding the mint account rent.programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.});const initializeMintInstruction = createInitializeMintInstruction(mint.publicKey, // Mint account to initialize.0, // Number of decimals for the token.feePayer.publicKey, // Authority allowed to mint new tokens.feePayer.publicKey, // Authority allowed to freeze token accounts.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.);const createTokenAccountInstruction = SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding the new token account.newAccountPubkey: tokenAccount.publicKey, // New token account to create.space: tokenAccountLen, // Account size in bytes for the token account plus ImmutableOwner.lamports: tokenAccountRent, // Lamports funding the token account rent.programId: TOKEN_2022_PROGRAM_ID // Program that owns the token account.});const initializeImmutableOwnerInstruction =createInitializeImmutableOwnerInstruction(tokenAccount.publicKey, // Token account that stores the ImmutableOwner extension.TOKEN_2022_PROGRAM_ID // Token program that owns the token account.);const initializeTokenAccountInstruction = createInitializeAccountInstruction(tokenAccount.publicKey, // Token account to initialize.mint.publicKey, // Mint for the token account.feePayer.publicKey, // Owner of the token account.TOKEN_2022_PROGRAM_ID // Token program that owns the token account.);await sendAndConfirmTransaction(connection,new Transaction().add(createMintAccountInstruction,initializeMintInstruction),[feePayer, mint]);await sendAndConfirmTransaction(connection,new Transaction().add(createTokenAccountInstruction,initializeImmutableOwnerInstruction,initializeTokenAccountInstruction),[feePayer, tokenAccount]);let failure: string | undefined;try {await sendAndConfirmTransaction(connection,new Transaction().add(createSetAuthorityInstruction(tokenAccount.publicKey, // Token account whose authority is being updated.feePayer.publicKey, // Current token account owner signing the instruction.AuthorityType.AccountOwner, // Account authority field to change.newOwner.publicKey, // New token account owner to set.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that owns the token account.)),[feePayer]);} catch (error) {failure = String(error);}if (!failure) {throw new Error("Expected the owner change to fail");}const tokenAccountData = await getAccount(connection,tokenAccount.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const immutableOwnerExtension = getImmutableOwner(tokenAccountData);console.log("Mint Address:", mint.publicKey.toBase58());console.log("Token Account:", tokenAccount.publicKey.toBase58());console.log("ImmutableOwner Extension:", immutableOwnerExtension);console.log("SetAuthority Failure:", failure);
Rust
use anyhow::{anyhow, Result};use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{program_pack::Pack,signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_token_2022_interface::{extension::{immutable_owner::ImmutableOwner, BaseStateWithExtensions, ExtensionType,StateWithExtensions,},instruction::{initialize_account, initialize_immutable_owner, initialize_mint, 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 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 = Mint::LEN;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space).await?;let create_mint_account_instruction = create_account(&fee_payer.pubkey(), // Account funding the new mint account.&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.&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.);let initialize_mint_instruction = 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.)?;let token_account = Keypair::new();let token_account_space =ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::ImmutableOwner])?;let token_account_rent = client.get_minimum_balance_for_rent_exemption(token_account_space).await?;let create_token_account_instruction = create_account(&fee_payer.pubkey(), // Account funding the new token account.&token_account.pubkey(), // New token account to create.token_account_rent, // Lamports funding the token account rent.token_account_space as u64, // Account size in bytes for the token account plus ImmutableOwner.&TOKEN_2022_PROGRAM_ID, // Program that owns the token account.);let initialize_token_account = initialize_account(&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.&token_account.pubkey(), // Token account to initialize.&mint.pubkey(), // Mint for the token account.&fee_payer.pubkey(), // Owner of the token account.)?;let init_immutable_owner_instruction =initialize_immutable_owner(&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.&token_account.pubkey(), // Token account that stores the ImmutableOwner extension.)?;let transaction = Transaction::new_signed_with_payer(&[create_mint_account_instruction,initialize_mint_instruction,create_token_account_instruction,init_immutable_owner_instruction,initialize_token_account,],Some(&fee_payer.pubkey()),&[&fee_payer, &mint, &token_account],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&transaction).await?;let token_account_data = client.get_account(&token_account.pubkey()).await?;let token_account_state = StateWithExtensions::<Account>::unpack(&token_account_data.data)?;let new_owner = Keypair::new();let set_authority_instruction = set_authority(&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.&token_account.pubkey(), // Token account whose authority is being updated.Some(&new_owner.pubkey()), // New token account owner to set.AuthorityType::AccountOwner, // Account authority field to change.&fee_payer.pubkey(), // Current token account owner signing the instruction.&[], // Additional multisig signers.)?;let set_authority_transaction = Transaction::new_signed_with_payer(&[set_authority_instruction],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);let failure = match client.send_and_confirm_transaction(&set_authority_transaction).await{Ok(sig) => return Err(anyhow!("Expected the owner change to fail: {}", sig)),Err(e) => format!("{:#?}", e),};let immutable_owner_extension = token_account_state.get_extension::<ImmutableOwner>()?;println!("Mint Address: {}", mint.pubkey());println!("Token Account: {}", token_account.pubkey());println!("ImmutableOwner Extension: {:#?}", immutable_owner_extension);println!("SetAuthority Failure: {}", failure);Ok(())}
Is this page helpful?