CPI Guard

Was ist CPI Guard?

Die CpiGuard-Kontoerweiterung des Token Extensions Program verhindert, dass ein anderes Onchain-Programm ein token account für bestimmte Token- Anweisungen über eine Cross Program Invocation (CPI) verwendet.

Wenn CPI Guard aktiviert ist, lehnt das Token Extensions Program diese Anweisungen ab, wenn ein anderes Onchain-Programm sie über CPI aufruft:

  • Transfer, TransferChecked und TransferCheckedWithFee, wenn der token account-Besitzer als Anweisungsautorität verwendet wird
  • Burn und BurnChecked, wenn der token account-Besitzer als Anweisungsautorität verwendet wird
  • Approve und ApproveChecked
  • CloseAccount, wenn lamport an eine andere Stelle als den token account-Besitzer gesendet würden
  • SetAuthority beim Hinzufügen oder Ersetzen der Schließungsautorität

Wenn CPI Guard aktiviert ist, sind diese Fälle weiterhin zulässig:

  • Transfer, TransferChecked, TransferCheckedWithFee, Burn und BurnChecked über CPI, wenn ein Delegat oder permanenter Delegat anstelle des token account-Besitzers signiert
  • CloseAccount über CPI, wenn lamport an den token account- Besitzer gesendet werden
  • SetAuthority zum Entfernen einer bestehenden Schließungsautorität
  • Revoke

Wenn der token account-Besitzer als Anweisungsautorität verwendet wird, können Token Extensions Program- Anweisungen weiterhin verarbeitet werden, wenn die Anweisung direkt zur Transaktion hinzugefügt wird, anstatt über CPI aufgerufen zu werden. Das Ändern des token account-Besitzers mit SetAuthority bleibt blockiert, während CPI Guard aktiviert ist, auch außerhalb von CPI. EnableCpiGuard und DisableCpiGuard können ebenfalls nicht über CPI aufgerufen werden.

So aktivieren Sie CPI Guard

Um CPI Guard zu aktivieren:

  1. Erstellen und initialisieren Sie eine Mint.
  2. Berechnen Sie die Größe des Token-Kontos und die benötigte Miete für ein Token-Konto mit CpiGuard.
  3. Erstellen und initialisieren Sie ein Token-Konto für die Mint.
  4. Rufen Sie EnableCpiGuard auf dem Token-Konto auf.
  5. Rufen Sie DisableCpiGuard auf dem Token-Konto auf, um diese Anweisungen erneut über CPI zuzulassen.

Mint erstellen und initialisieren

Erstellen und initialisieren Sie die Mint, die das Token-Konto verwenden wird.

Token-Konto-Größe berechnen

Berechnen Sie die Token-Konto-Größe für das Basis-Token-Konto plus der CpiGuard-Erweiterung. Dies ist die Größe, die in CreateAccount verwendet wird.

Miete berechnen

Berechnen Sie die Miete anhand der für die CpiGuard-Erweiterung benötigten Token-Konto-Größe.

Token-Konto erstellen und initialisieren

Erstellen Sie das Token-Konto mit dem berechneten Speicherplatz und den Lamports und initialisieren Sie es dann für die Mint.

CPI-Schutz aktivieren

Aktivieren Sie CpiGuard auf dem Token-Konto.

CPI-Schutz deaktivieren

Deaktivieren Sie CpiGuard auf dem Token-Konto.

Mint erstellen und initialisieren

Erstellen und initialisieren Sie die Mint, die das Token-Konto verwenden wird.

Token-Konto-Größe berechnen

Berechnen Sie die Token-Konto-Größe für das Basis-Token-Konto plus der CpiGuard-Erweiterung. Dies ist die Größe, die in CreateAccount verwendet wird.

Miete berechnen

Berechnen Sie die Miete anhand der für die CpiGuard-Erweiterung benötigten Token-Konto-Größe.

Token-Konto erstellen und initialisieren

Erstellen Sie das Token-Konto mit dem berechneten Speicherplatz und den Lamports und initialisieren Sie es dann für die Mint.

CPI-Schutz aktivieren

Aktivieren Sie CpiGuard auf dem Token-Konto.

CPI-Schutz deaktivieren

Deaktivieren Sie CpiGuard auf dem Token-Konto.

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

Quellreferenz

ElementBeschreibungQuelle
CpiGuardKonten-Erweiterung, die speichert, ob CPI Guard derzeit für das token account aktiviert ist.Quelle
CpiGuardInstruction::EnableAnweisungen, die CPI Guard auf einem token account aktivieren.Quelle
CpiGuardInstruction::DisableAnweisungen, die CPI Guard auf einem token account deaktivieren.Quelle
process_toggle_cpi_guardProzessor-Logik, die von Enable und Disable verwendet wird, um die Konto-Eigentümerschaft zu validieren, CPI-Änderungen am Guard selbst abzulehnen und CpiGuard zu initialisieren, falls noch nicht vorhanden, bevor lock_cpi gesetzt wird.Quelle

Typescript

Das Kit-Beispiel unten 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 {
extension,
fetchToken,
getDisableCpiGuardInstruction,
getEnableCpiGuardInstruction,
getInitializeAccountInstruction,
getInitializeMintInstruction,
getMintSize,
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 mintSpace = BigInt(getMintSize());
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 account.
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 cpiGuardExtension = extension("CpiGuard", {
lockCpi: false
});
const tokenSpace = BigInt(getTokenSize([cpiGuardExtension]));
const tokenRent = await client.rpc
.getMinimumBalanceForRentExemption(tokenSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer, // Account funding account creation.
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 CpiGuard.
programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the token account.
}),
getInitializeAccountInstruction({
account: tokenAccount.address, // Token account to initialize.
mint: mint.address, // Mint for the token account.
owner: client.payer.address // Owner allowed to control the token account.
})
]);
await client.sendTransaction([
getEnableCpiGuardInstruction({
token: tokenAccount.address, // Token account that stores the CpiGuard extension.
owner: client.payer // Token account owner authorized to enable CPI guard.
})
]);
const enabledTokenAccount = await fetchToken(client.rpc, tokenAccount.address);
const enabledCpiGuard = (
unwrapOption(enabledTokenAccount.data.extensions) ?? []
).find((item) => isExtension("CpiGuard", item));
await client.sendTransaction([
getDisableCpiGuardInstruction({
token: tokenAccount.address, // Token account that stores the CpiGuard extension.
owner: client.payer // Token account owner authorized to disable CPI guard.
})
]);
const disabledTokenAccount = await fetchToken(client.rpc, tokenAccount.address);
const disabledCpiGuard = (
unwrapOption(disabledTokenAccount.data.extensions) ?? []
).find((item) => isExtension("CpiGuard", item));
console.log("\nMint Address:", mint.address);
console.log("\nToken Account:", tokenAccount.address);
console.log("\nEnabled CPI Guard:", enabledCpiGuard);
console.log("\nDisabled CPI Guard:", disabledCpiGuard);
Console
Click to execute the code.

Web3.js

Instructions
import {
Connection,
Keypair,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction,
SystemProgram,
Transaction
} from "@solana/web3.js";
import {
createDisableCpiGuardInstruction,
createEnableCpiGuardInstruction,
createInitializeAccountInstruction,
createInitializeMintInstruction,
ExtensionType,
getAccountLen,
getAccount,
getCpiGuard,
getMintLen,
TOKEN_2022_PROGRAM_ID
} from "@solana/spl-token";
const connection = new Connection("http://localhost:8899", "confirmed");
const latestBlockhash = await connection.getLatestBlockhash();
const feePayer = Keypair.generate();
const mint = Keypair.generate();
const tokenAccount = Keypair.generate();
const airdropSignature = await connection.requestAirdrop(
feePayer.publicKey,
5 * LAMPORTS_PER_SOL
);
await connection.confirmTransaction({
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
signature: airdropSignature
});
const mintSpace = getMintLen([]);
const mintRent = await connection.getMinimumBalanceForRentExemption(mintSpace);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
SystemProgram.createAccount({
fromPubkey: feePayer.publicKey, // Account funding account creation.
newAccountPubkey: mint.publicKey, // New mint account to create.
space: mintSpace, // Account size in bytes for the mint account.
lamports: mintRent, // Lamports funding the mint account rent.
programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
}),
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.
)
),
[feePayer, mint],
{ commitment: "confirmed" }
);
const tokenAccountSpace = getAccountLen([ExtensionType.CpiGuard]);
const tokenAccountRent =
await connection.getMinimumBalanceForRentExemption(tokenAccountSpace);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
SystemProgram.createAccount({
fromPubkey: feePayer.publicKey, // Account funding account creation.
newAccountPubkey: tokenAccount.publicKey, // New token account to create.
space: tokenAccountSpace, // Account size in bytes for the token account plus CpiGuard.
lamports: tokenAccountRent, // Lamports funding the token account rent.
programId: TOKEN_2022_PROGRAM_ID // Program that owns the token account.
}),
createInitializeAccountInstruction(
tokenAccount.publicKey, // Token account to initialize.
mint.publicKey, // Mint for the token account.
feePayer.publicKey, // Owner allowed to control the token account.
TOKEN_2022_PROGRAM_ID // Program that owns the token account.
)
),
[feePayer, tokenAccount],
{ commitment: "confirmed" }
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
createEnableCpiGuardInstruction(
tokenAccount.publicKey, // Token account that stores the CpiGuard extension.
feePayer.publicKey, // Token account owner authorized to enable CPI guard.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that owns the token account.
)
),
[feePayer],
{ commitment: "confirmed" }
);
const enabledTokenAccount = await getAccount(
connection,
tokenAccount.publicKey,
"confirmed",
TOKEN_2022_PROGRAM_ID
);
const enabledCpiGuard = getCpiGuard(enabledTokenAccount);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
createDisableCpiGuardInstruction(
tokenAccount.publicKey, // Token account that stores the CpiGuard extension.
feePayer.publicKey, // Token account owner authorized to disable CPI guard.
[], // Additional multisig signers.
TOKEN_2022_PROGRAM_ID // Token program that owns the token account.
)
),
[feePayer],
{ commitment: "confirmed" }
);
const disabledTokenAccount = await getAccount(
connection,
tokenAccount.publicKey,
"confirmed",
TOKEN_2022_PROGRAM_ID
);
const disabledCpiGuard = getCpiGuard(disabledTokenAccount);
console.log("\nMint Address:", mint.publicKey.toBase58());
console.log("\nToken Account:", tokenAccount.publicKey.toBase58());
console.log("\nEnabled CPI Guard:", enabledCpiGuard);
console.log("\nDisabled CPI Guard:", disabledCpiGuard);
Console
Click to execute the code.

Rust

Rust
use 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::{
cpi_guard::{
instruction::{disable_cpi_guard, enable_cpi_guard},
CpiGuard,
},
BaseStateWithExtensions, ExtensionType, StateWithExtensions,
},
instruction::{initialize_account, initialize_mint},
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 token_account = Keypair::new();
let mint_space = Mint::LEN;
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 account.
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
),
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 token_account_space =
ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::CpiGuard])?;
let token_account_rent = client
.get_minimum_balance_for_rent_exemption(token_account_space)
.await?;
let create_token_account_transaction = Transaction::new_signed_with_payer(
&[
create_account(
&fee_payer.pubkey(), // Account funding account creation.
&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 CpiGuard.
&TOKEN_2022_PROGRAM_ID, // Program that owns the token account.
),
initialize_account(
&TOKEN_2022_PROGRAM_ID, // Program that owns the token account.
&token_account.pubkey(), // Token account to initialize.
&mint.pubkey(), // Mint for the token account.
&fee_payer.pubkey(), // Owner allowed to control the token account.
)?,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &token_account],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&create_token_account_transaction)
.await?;
let enable_cpi_guard_instruction = enable_cpi_guard(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
&token_account.pubkey(), // Token account that stores the CpiGuard extension.
&fee_payer.pubkey(), // Token account owner authorized to enable CPI guard.
&[], // Additional multisig signers.
)?;
let enable_transaction = Transaction::new_signed_with_payer(
&[enable_cpi_guard_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&enable_transaction)
.await?;
let enabled_token_account_data = client.get_account(&token_account.pubkey()).await?;
let enabled_token_account_state =
StateWithExtensions::<Account>::unpack(&enabled_token_account_data.data)?;
let enabled_cpi_guard = enabled_token_account_state.get_extension::<CpiGuard>()?;
let disable_cpi_guard_instruction = disable_cpi_guard(
&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.
&token_account.pubkey(), // Token account that stores the CpiGuard extension.
&fee_payer.pubkey(), // Token account owner authorized to disable CPI guard.
&[], // Additional multisig signers.
)?;
let disable_transaction = Transaction::new_signed_with_payer(
&[disable_cpi_guard_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&disable_transaction)
.await?;
let disabled_token_account_data = client.get_account(&token_account.pubkey()).await?;
let disabled_token_account_state =
StateWithExtensions::<Account>::unpack(&disabled_token_account_data.data)?;
let disabled_cpi_guard = disabled_token_account_state.get_extension::<CpiGuard>()?;
println!("\nMint Address: {}", mint.pubkey());
println!("\nToken Account: {}", token_account.pubkey());
println!("\nEnabled CPI Guard: {:#?}", enabled_cpi_guard);
println!("\nDisabled CPI Guard: {:#?}", disabled_cpi_guard);
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