Unveränderlicher Eigentümer

Was ist Immutable Owner?

Die ImmutableOwner-Kontenerweiterung des Token Extensions Program macht den Eigentümer eines Tokenkontos dauerhaft.

Sobald ein Tokenkonto mit ImmutableOwner initialisiert wurde, können spätere SetAuthority- Anweisungen dessen AccountOwner-Autorität nicht mehr ändern.

Associated Token Accounts

Associated token accounts, die über das Associated Token Program für das Token Extensions Program erstellt wurden, initialisieren automatisch ImmutableOwner auf dem Tokenkonto.

So initialisieren Sie ein Immutable Owner-Konto

Um ein Immutable Owner-Konto zu initialisieren:

  1. Erstellen und initialisieren Sie einen Mint.
  2. Erstellen Sie ein Tokenkonto mit ausreichend Platz für ImmutableOwner.
  3. Initialisieren Sie ImmutableOwner auf dem Tokenkonto vor InitializeAccount.
  4. Der Versuch, den Konteninhaber später mit SetAuthority zu ändern, schlägt mit TokenError::ImmutableOwner fehl.

Tokenkontogröße berechnen

Berechnen Sie die Tokenkontogröße für das Basis-Tokenkonto plus die ImmutableOwner-Erweiterung. Dies ist die Größe, die in CreateAccount verwendet wird.

Rent berechnen

Berechnen Sie rent unter Verwendung der Tokenkontogröße, die für die ImmutableOwner-Erweiterung benötigt wird.

Tokenkonto erstellen

Erstellen Sie das Tokenkonto mit dem berechneten Speicherplatz und lamport.

ImmutableOwner initialisieren

Initialisieren Sie die ImmutableOwner-Erweiterung auf dem Token-Konto.

Das Token-Konto initialisieren

Initialisieren Sie das Token-Konto mit InitializeAccount in derselben Transaktion.

Tokenkontogröße berechnen

Berechnen Sie die Tokenkontogröße für das Basis-Tokenkonto plus die ImmutableOwner-Erweiterung. Dies ist die Größe, die in CreateAccount verwendet wird.

Rent berechnen

Berechnen Sie rent unter Verwendung der Tokenkontogröße, die für die ImmutableOwner-Erweiterung benötigt wird.

Tokenkonto erstellen

Erstellen Sie das Tokenkonto mit dem berechneten Speicherplatz und lamport.

ImmutableOwner initialisieren

Initialisieren Sie die ImmutableOwner-Erweiterung auf dem Token-Konto.

Das Token-Konto initialisieren

Initialisieren Sie das Token-Konto mit InitializeAccount in derselben Transaktion.

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 tokenAccount = await generateKeyPairSigner();
const mintSpace = BigInt(getMintSize());
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
}),
getInitializeMintInstruction({
mint: mint.address,
decimals: 0,
mintAuthority: client.payer.address,
freezeAuthority: client.payer.address
})
]);
const immutableOwnerExtension = extension("ImmutableOwner", {});
const tokenSpace = BigInt(getTokenSize([immutableOwnerExtension]));

Reihenfolge der Anweisungen

InitializeImmutableOwner muss vor InitializeAccount kommen. CreateAccount, InitializeImmutableOwner und InitializeAccount müssen in derselben Transaktion enthalten sein.

Quellenreferenz

ElementBeschreibungQuelle
ImmutableOwnerKonten-Erweiterung, die einen Token-Konto-Eigentümer als unveränderlich markiert.Quelle
InitializeImmutableOwnerAnweisung, die die unveränderlicher-Eigentümer-Erweiterung auf einem nicht initialisierten Token-Konto initialisiert.Quelle
process_initialize_immutable_ownerProzessor-Logik, die ImmutableOwner auf dem Konto zuweist und initialisiert.Quelle
SetAuthorityBasis-Token-Anweisung zum Ändern von Berechtigungen, einschließlich eines Token-Konto-Eigentümers.Quelle
process_set_authorityProzessor-Logik, die Eigentümeränderungen ablehnt, wenn das Token-Konto ImmutableOwner aktiviert hat.Quelle

Typescript

Das folgende Kit-Beispiel verwendet die generierten Anweisungen direkt. Legacy-Beispiele mit @solana/web3.js und @solana/spl-token sind als Referenz enthalten.

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,
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);
Console
Click to execute the code.

Web3.js

Instructions
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);
Console
Click to execute the code.

Rust

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(())
}
Console
Click to execute the code.

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten

Verwaltet von

© 2026 Solana Foundation.
Alle Rechte vorbehalten.
Verbinden Sie sich