CPI Guard

Apa Itu CPI Guard?

Ekstensi akun CpiGuard dari Token Extensions Program mencegah program onchain lain menggunakan token account untuk instruksi token tertentu melalui cross-program invocation (CPI).

Ketika CPI guard diaktifkan, Token Extensions Program menolak instruksi-instruksi berikut ketika program onchain lain memanggilnya melalui CPI:

  • Transfer, TransferChecked, dan TransferCheckedWithFee ketika pemilik token account digunakan sebagai otoritas instruksi
  • Burn dan BurnChecked ketika pemilik token account digunakan sebagai otoritas instruksi
  • Approve dan ApproveChecked
  • CloseAccount ketika lamport akan dikirim ke tempat lain selain pemilik token account
  • SetAuthority ketika menambahkan atau mengganti otoritas penutupan

Ketika CPI guard diaktifkan, kasus-kasus berikut masih diizinkan:

  • Transfer, TransferChecked, TransferCheckedWithFee, Burn, dan BurnChecked melalui CPI ketika delegasi atau delegasi permanen menandatangani alih-alih pemilik token account
  • CloseAccount melalui CPI ketika lamport dikirim ke pemilik token account
  • SetAuthority untuk menghapus otoritas penutupan yang sudah ada
  • Revoke

Ketika pemilik token account digunakan sebagai otoritas instruksi, instruksi Token Extensions Program masih dapat diproses jika instruksi ditambahkan langsung ke transaksi alih-alih dipanggil melalui CPI. Mengubah pemilik token account dengan SetAuthority tetap diblokir selama CPI guard diaktifkan, bahkan di luar CPI. EnableCpiGuard dan DisableCpiGuard juga tidak dapat dipanggil melalui CPI.

Cara Mengaktifkan CPI Guard

Untuk mengaktifkan CPI guard:

  1. Buat dan inisialisasi mint.
  2. Hitung ukuran token account dan rent yang diperlukan untuk token account dengan CpiGuard.
  3. Buat dan inisialisasi token account untuk mint.
  4. Panggil EnableCpiGuard pada token account.
  5. Panggil DisableCpiGuard pada token account untuk mengizinkan instruksi tersebut melalui CPI lagi.

Buat dan inisialisasi mint

Buat dan inisialisasi mint yang akan digunakan oleh token account.

Hitung ukuran token account

Hitung ukuran token account untuk token account dasar ditambah ekstensi CpiGuard. Ini adalah ukuran yang digunakan dalam CreateAccount.

Hitung rent

Hitung rent menggunakan ukuran token account yang diperlukan untuk ekstensi CpiGuard.

Buat dan inisialisasi token account

Buat token account dengan space dan lamport yang telah dihitung, kemudian inisialisasi untuk mint.

Aktifkan CPI guard

Aktifkan CpiGuard pada token account.

Nonaktifkan CPI guard

Nonaktifkan CpiGuard pada token account.

Buat dan inisialisasi mint

Buat dan inisialisasi mint yang akan digunakan oleh token account.

Hitung ukuran token account

Hitung ukuran token account untuk token account dasar ditambah ekstensi CpiGuard. Ini adalah ukuran yang digunakan dalam CreateAccount.

Hitung rent

Hitung rent menggunakan ukuran token account yang diperlukan untuk ekstensi CpiGuard.

Buat dan inisialisasi token account

Buat token account dengan space dan lamport yang telah dihitung, kemudian inisialisasi untuk mint.

Aktifkan CPI guard

Aktifkan CpiGuard pada token account.

Nonaktifkan CPI guard

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

Referensi Sumber

ItemDeskripsiSumber
CpiGuardEkstensi akun yang menyimpan informasi apakah CPI guard saat ini diaktifkan untuk token account.Sumber
CpiGuardInstruction::EnableInstruksi yang mengaktifkan CPI guard pada token account.Sumber
CpiGuardInstruction::DisableInstruksi yang menonaktifkan CPI guard pada token account.Sumber
process_toggle_cpi_guardLogika prosesor yang digunakan oleh Enable dan Disable yang memvalidasi kepemilikan akun, menolak perubahan CPI terhadap guard itu sendiri, dan menginisialisasi CpiGuard jika belum ada sebelum mengatur lock_cpi.Sumber

Typescript

Contoh Kit di bawah ini menggunakan instruksi yang dihasilkan secara langsung. Contoh lawas menggunakan @solana/web3.js dan @solana/spl-token disertakan sebagai referensi.

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?

Daftar Isi

Edit Halaman

Dikelola oleh

© 2026 Yayasan Solana.
Semua hak dilindungi.
Terhubung