Wat is CPI Guard?
De CpiGuard-accountextensie van het Token Extensions Program voorkomt dat
een ander onchain-programma een token account gebruikt voor bepaalde
tokeninstructies via een Cross Program Invocation (CPI).
Wanneer CPI-beveiliging is ingeschakeld, wijst het Token Extensions Program deze instructies af wanneer een ander onchain-programma ze aanroept via CPI:
Transfer,TransferCheckedenTransferCheckedWithFeewanneer de eigenaar van het token account wordt gebruikt als instructie-autoriteitBurnenBurnCheckedwanneer de eigenaar van het token account wordt gebruikt als instructie-autoriteitApproveenApproveCheckedCloseAccountwanneer lamports ergens anders heen worden gestuurd dan naar de eigenaar van het token accountSetAuthoritybij het toevoegen of vervangen van de sluitingsautoriteit
Wanneer CPI-beveiliging is ingeschakeld, zijn deze gevallen nog steeds toegestaan:
Transfer,TransferChecked,TransferCheckedWithFee,BurnenBurnCheckedvia CPI wanneer een gemachtigde of permanente gemachtigde ondertekent in plaats van de eigenaar van het token accountCloseAccountvia CPI wanneer lamports naar de eigenaar van het token account worden gestuurdSetAuthorityom een bestaande sluitingsautoriteit te verwijderenRevoke
Wanneer de eigenaar van het token account wordt gebruikt als
instructie-autoriteit, kunnen Token Extension Program-instructies nog steeds
worden verwerkt als de instructie rechtstreeks aan de transactie wordt
toegevoegd in plaats van te worden aangeroepen via CPI. Het wijzigen van de
eigenaar van het token account met SetAuthority blijft geblokkeerd zolang
CPI-beveiliging is ingeschakeld, zelfs buiten CPI. EnableCpiGuard en
DisableCpiGuard kunnen eveneens niet worden aangeroepen via CPI.
Hoe CPI Guard in te schakelen
Om CPI-beveiliging in te schakelen:
- Creëer en initialiseer een mint.
- Bereken de grootte van het token account en de benodigde rent voor een token
account met
CpiGuard. - Creëer en initialiseer een token account voor de mint.
- Roep
EnableCpiGuardaan op het token account. - Roep
DisableCpiGuardaan op het token account om die instructies opnieuw via CPI toe te staan.
Creëer en initialiseer de mint
Creëer en initialiseer de mint die het token account zal gebruiken.
Bereken grootte token account
Bereken de grootte van het token account voor het basis token account plus de
CpiGuard extensie. Dit is de grootte die wordt gebruikt in
CreateAccount.
Bereken rent
Bereken rent met behulp van de grootte van het token account die nodig is voor
de CpiGuard extensie.
Creëer en initialiseer het token account
Creëer het token account met de berekende ruimte en lamports, en initialiseer het vervolgens voor de mint.
Schakel CPI guard in
Schakel CpiGuard in op het token account.
Schakel CPI guard uit
Schakel CpiGuard uit op het token account.
Bronverwijzing
| Item | Beschrijving | Bron |
|---|---|---|
CpiGuard | Accountextensie die opslaat of CPI guard momenteel is ingeschakeld voor het token account. | Bron |
CpiGuardInstruction::Enable | Instructie die CPI guard inschakelt op een token account. | Bron |
CpiGuardInstruction::Disable | Instructie die CPI guard uitschakelt op een token account. | Bron |
process_toggle_cpi_guard | Processorlogica gebruikt door Enable en Disable die accounteigendom valideert, CPI-wijzigingen aan de guard zelf afwijst en CpiGuard initialiseert indien nog niet aanwezig voordat lock_cpi wordt ingesteld. | Bron |
Typescript
Het onderstaande Kit-voorbeeld maakt gebruik van de gegenereerde instructies
direct. Legacy-voorbeelden met @solana/web3.js en @solana/spl-token zijn ter
referentie opgenomen.
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?