CPI Guard

Τι είναι το CPI Guard;

Η επέκταση λογαριασμού CpiGuard του Token Extensions Program εμποδίζει ένα άλλο onchain πρόγραμμα να χρησιμοποιήσει έναν token account για συγκεκριμένες εντολές token μέσω Cross Program Invocation (CPI).

Όταν το CPI guard είναι ενεργοποιημένο, το Token Extensions Program απορρίπτει αυτές τις εντολές όταν ένα άλλο onchain πρόγραμμα τις καλεί μέσω CPI:

  • Transfer, TransferChecked και TransferCheckedWithFee όταν ο κάτοχος του token account χρησιμοποιείται ως εξουσιοδότηση της εντολής
  • Burn και BurnChecked όταν ο κάτοχος του token account χρησιμοποιείται ως εξουσιοδότηση της εντολής
  • Approve και ApproveChecked
  • CloseAccount όταν θα αποστέλλονταν lamport κάπου αλλού εκτός από τον κάτοχο του token account
  • SetAuthority κατά την προσθήκη ή αντικατάσταση της εξουσιοδότησης κλεισίματος

Όταν το CPI guard είναι ενεργοποιημένο, αυτές οι περιπτώσεις εξακολουθούν να επιτρέπονται:

  • Transfer, TransferChecked, TransferCheckedWithFee, Burn και BurnChecked μέσω CPI όταν υπογράφει ένας delegate ή permanent delegate αντί του κατόχου του token account
  • CloseAccount μέσω CPI όταν τα lamport αποστέλλονται στον κάτοχο του token account
  • SetAuthority για την αφαίρεση μιας υπάρχουσας εξουσιοδότησης κλεισίματος
  • Revoke

Όταν ο κάτοχος του token account χρησιμοποιείται ως εξουσιοδότηση της εντολής, οι εντολές του Token Extensions Program μπορούν ακόμα να επεξεργαστούν εάν η εντολή προστεθεί άμεσα στη συναλλαγή αντί να κληθεί μέσω CPI. Η αλλαγή του κατόχου του token account με SetAuthority παραμένει αποκλεισμένη όσο το CPI guard είναι ενεργοποιημένο, ακόμα και εκτός CPI. Οι EnableCpiGuard και DisableCpiGuard επίσης δεν μπορούν να κληθούν μέσω CPI.

Πώς να Ενεργοποιήσετε το CPI Guard

Για να ενεργοποιήσετε το CPI guard:

  1. Δημιουργήστε και αρχικοποιήστε ένα mint.
  2. Υπολογίστε το μέγεθος του token account και το rent που απαιτείται για ένα token account με CpiGuard.
  3. Δημιουργήστε και αρχικοποιήστε ένα token account για το mint.
  4. Καλέστε την EnableCpiGuard στο token account.
  5. Καλέστε την DisableCpiGuard στο token account για να επιτρέψετε αυτές τις οδηγίες μέσω CPI ξανά.

Δημιουργήστε και αρχικοποιήστε το mint

Δημιουργήστε και αρχικοποιήστε το mint που θα χρησιμοποιήσει το token account.

Υπολογίστε το μέγεθος του token account

Υπολογίστε το μέγεθος του token account για το βασικό token account συν την επέκταση CpiGuard. Αυτό είναι το μέγεθος που χρησιμοποιείται στην CreateAccount.

Υπολογίστε το rent

Υπολογίστε το rent χρησιμοποιώντας το μέγεθος του token account που απαιτείται για την επέκταση CpiGuard.

Δημιουργήστε και αρχικοποιήστε το token account

Δημιουργήστε το token account με τον υπολογισμένο χώρο και τα lamports, και στη συνέχεια αρχικοποιήστε το για το mint.

Ενεργοποιήστε το CPI guard

Ενεργοποιήστε το CpiGuard στο token account.

Απενεργοποιήστε το CPI guard

Απενεργοποιήστε το CpiGuard στο token account.

Δημιουργήστε και αρχικοποιήστε το mint

Δημιουργήστε και αρχικοποιήστε το mint που θα χρησιμοποιήσει το token account.

Υπολογίστε το μέγεθος του token account

Υπολογίστε το μέγεθος του token account για το βασικό token account συν την επέκταση CpiGuard. Αυτό είναι το μέγεθος που χρησιμοποιείται στην CreateAccount.

Υπολογίστε το rent

Υπολογίστε το rent χρησιμοποιώντας το μέγεθος του token account που απαιτείται για την επέκταση CpiGuard.

Δημιουργήστε και αρχικοποιήστε το token account

Δημιουργήστε το token account με τον υπολογισμένο χώρο και τα lamports, και στη συνέχεια αρχικοποιήστε το για το mint.

Ενεργοποιήστε το CPI guard

Ενεργοποιήστε το CpiGuard στο token account.

Απενεργοποιήστε το CPI guard

Απενεργοποιήστε το CpiGuard στο token account.

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

Αναφορά Πηγαίου Κώδικα

ΣτοιχείοΠεριγραφήΠηγή
CpiGuardΕπέκταση λογαριασμού που αποθηκεύει εάν το CPI guard είναι ενεργοποιημένο για τον token account.Πηγή
CpiGuardInstruction::EnableΕντολή που ενεργοποιεί το CPI guard σε έναν token account.Πηγή
CpiGuardInstruction::DisableΕντολή που απενεργοποιεί το CPI guard σε έναν token account.Πηγή
process_toggle_cpi_guardΛογική επεξεργαστή που χρησιμοποιείται από τις Enable και Disable που επικυρώνει την κυριότητα λογαριασμού, απορρίπτει αλλαγές CPI στο ίδιο το guard και αρχικοποιεί το CpiGuard εάν δεν υπάρχει ήδη πριν ορίσει το lock_cpi.Πηγή

Typescript

Το παρακάτω παράδειγμα Kit χρησιμοποιεί απευθείας τις δημιουργημένες εντολές. Παλαιότερα παραδείγματα που χρησιμοποιούν @solana/web3.js και @solana/spl-token περιλαμβάνονται για αναφορά.

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?

Πίνακας Περιεχομένων

Επεξεργασία Σελίδας

Διαχειρίζεται από

© 2026 Ίδρυμα Solana.
Με επιφύλαξη παντός δικαιώματος.
Συνδεθείτε