ما هو CPI Guard؟
امتداد حساب CpiGuard في برنامج Token Extensions يمنع البرامج الأخرى على
السلسلة من استخدام حساب الرموز لتعليمات رموز معينة من خلال الاستدعاء عبر البرامج
(Cross Program Invocation).
عند تفعيل CPI guard، يرفض Token Extensions Program هذه التعليمات عندما يستدعيها برنامج آخر على السلسلة من خلال CPI:
Transfer،TransferChecked، وTransferCheckedWithFeeعندما يتم استخدام مالك حساب الرموز كسلطة للتعليمةBurnوBurnCheckedعندما يتم استخدام مالك حساب الرموز كسلطة للتعليمةApproveوApproveCheckedCloseAccountعندما يتم إرسال lamport إلى مكان آخر غير مالك حساب الرموزSetAuthorityعند إضافة أو استبدال سلطة الإغلاق
عند تفعيل CPI guard، تظل هذه الحالات مسموحة:
Transfer،TransferChecked،TransferCheckedWithFee،Burn، وBurnCheckedمن خلال CPI عندما يوقع مفوض أو مفوض دائم بدلاً من مالك حساب الرموزCloseAccountمن خلال CPI عندما يتم إرسال lamport إلى مالك حساب الرموزSetAuthorityلإزالة سلطة إغلاق موجودةRevoke
عند استخدام مالك حساب الرموز كسلطة للتعليمة، لا تزال تعليمات Token Extensions
Program قابلة للمعالجة إذا تمت إضافة التعليمة مباشرة إلى المعاملة بدلاً من
استدعائها من خلال CPI. يظل تغيير مالك حساب الرموز باستخدام SetAuthority
محظوراً أثناء تفعيل CPI guard، حتى خارج CPI. لا يمكن أيضاً استدعاء
EnableCpiGuard و DisableCpiGuard من خلال CPI.
كيفية تفعيل CPI Guard
لتفعيل CPI guard:
- إنشاء وتهيئة mint.
- حساب حجم حساب الرمز المميز و rent المطلوب لحساب رمز مميز مع
CpiGuard. - إنشاء وتهيئة حساب رمز مميز للـ mint.
- استدعاء
EnableCpiGuardعلى حساب الرمز المميز. - استدعاء
DisableCpiGuardعلى حساب الرمز المميز للسماح بتلك التعليمات من خلال CPI مرة أخرى.
إنشاء وتهيئة الـ mint
إنشاء وتهيئة الـ mint الذي سيستخدمه حساب الرمز المميز.
حساب حجم حساب الرمز المميز
حساب حجم حساب الرمز المميز لحساب الرمز المميز الأساسي بالإضافة إلى امتداد
CpiGuard. هذا هو الحجم المستخدم في CreateAccount.
حساب rent
حساب rent باستخدام حجم حساب الرمز المميز المطلوب لامتداد CpiGuard.
إنشاء وتهيئة حساب الرمز المميز
إنشاء حساب الرمز المميز بالمساحة و lamport المحسوبة، ثم تهيئته للـ mint.
تفعيل حماية CPI
تفعيل CpiGuard على حساب الرمز المميز.
تعطيل حماية CPI
تعطيل CpiGuard على حساب الرمز المميز.
مرجع المصدر
| العنصر | الوصف | المصدر |
|---|---|---|
CpiGuard | امتداد الحساب الذي يخزن ما إذا كان حارس CPI مفعلاً حالياً لحساب الرمز المميز. | المصدر |
CpiGuardInstruction::Enable | التعليمة التي تفعّل حارس CPI على حساب الرمز المميز. | المصدر |
CpiGuardInstruction::Disable | التعليمة التي تعطّل حارس CPI على حساب الرمز المميز. | المصدر |
process_toggle_cpi_guard | منطق المعالج المستخدم بواسطة Enable و Disable الذي يتحقق من ملكية الحساب، يرفض تغييرات CPI على الحارس نفسه، ويهيئ CpiGuard إذا لم يكن موجوداً بالفعل قبل تعيين lock_cpi. | المصدر |
Typescript
يستخدم مثال Kit أدناه التعليمات المولدة مباشرةً. تم تضمين الأمثلة القديمة
باستخدام @solana/web3.js و @solana/spl-token للرجوع إليها.
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 {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);
Web3.js
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);
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(())}
Is this page helpful?