Extensions de métadonnées et pointeur de métadonnées

Comment activer l'extension MetadataPointerExtension et l'extension TokenMetadata

La MetadataPointerExtension permet à un mint account de spécifier l'adresse de son compte de métadonnées. Ce pointeur peut référencer n'importe quel compte détenu par un programme implémentant l'interface Token Metadata, offrant ainsi une flexibilité quant à l'emplacement de stockage des métadonnées.

Le Token Extensions Program implémente directement l'interface Token Metadata, permettant ainsi de stocker les métadonnées sur le mint account lui-même. Cela inclut le nom du token, son symbole, son URI et des données descriptives supplémentaires. Lorsque les extensions de pointeur pointent vers le mint lui-même, les extensions TokenMetadata peuvent être utilisées pour stocker toutes les métadonnées dans le mint account.

Typescript

import { getCreateAccountInstruction } from "@solana-program/system";
import {
extension,
getInitializeAccountInstruction,
getInitializeMintInstruction,
getInitializeMetadataPointerInstruction,
getMintSize,
getTokenSize,
TOKEN_2022_PROGRAM_ADDRESS,
getInitializeTokenMetadataInstruction
} from "@solana-program/token-2022";
import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners,
some
} from "@solana/kit";
// Create Connection, local validator in this example
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate the authority for the mint (also acts as fee payer)
const authority = await generateKeyPairSigner();
// Fund authority/fee payer
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: authority.address,
lamports: lamports(5_000_000_000n), // 5 SOL
commitment: "confirmed"
});
// Generate keypair to use as address of mint
const mint = await generateKeyPairSigner();
// Enable Metadata and Metadata Pointer extensions
const metadataExtension = extension("TokenMetadata", {
updateAuthority: some(authority.address),
mint: mint.address,
name: "OPOS",
symbol: "OPS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
additionalMetadata: new Map().set("description", "Only possible on Solana")
});
const metadataPointerExtension = extension("MetadataPointer", {
authority: authority.address,
metadataAddress: mint.address // can also point to another account if desired
});
// Get mint account size with the metadata pointer extension alone
const spaceWithoutTokenMetadataExtension = BigInt(
getMintSize([metadataPointerExtension])
);
// Get mint account size with all extensions(metadata && metadataPointer)
const spaceWithTokenMetadataExtension = BigInt(
getMintSize([metadataPointerExtension, metadataExtension])
);
// Get minimum balance for rent exemption
const rent = await rpc
.getMinimumBalanceForRentExemption(spaceWithTokenMetadataExtension)
.send();
// Get latest blockhash to include in transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Instruction to create new account for mint
const createMintAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: mint,
lamports: rent,
space: spaceWithoutTokenMetadataExtension,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
});
// Initialize metadata extension
const initializeMetadataInstruction = getInitializeTokenMetadataInstruction({
metadata: mint.address, // Account address that holds the metadata
updateAuthority: authority.address, // Authority that can update the metadata
mint: mint.address, // Mint Account address
mintAuthority: authority, // Designated Mint Authority
name: "OPOS",
symbol: "OPS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json"
});
// Initialize metadata pointer extension
const initializeMetadataPointerInstruction =
getInitializeMetadataPointerInstruction({
mint: mint.address,
authority: authority.address,
metadataAddress: mint.address
});
// Initialize mint account data
const initializeMintInstruction = getInitializeMintInstruction({
mint: mint.address,
decimals: 9,
mintAuthority: authority.address,
freezeAuthority: authority.address
});
// Generate keypair to use as address of token account
const tokenAccount = await generateKeyPairSigner();
// Get token account size (basic)
const tokenAccountLen = BigInt(getTokenSize([]));
// Get minimum balance for rent exemption
const tokenAccountRent = await rpc
.getMinimumBalanceForRentExemption(tokenAccountLen)
.send();
// Instruction to create new token account
const createTokenAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: tokenAccount,
lamports: tokenAccountRent,
space: tokenAccountLen,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
});
// Instruction to initialize the created token account
const initializeTokenAccountInstruction = getInitializeAccountInstruction({
account: tokenAccount.address,
mint: mint.address,
owner: authority.address
});
// Build the instruction list
const instructions = [
createMintAccountInstruction,
initializeMetadataPointerInstruction,
initializeMintInstruction,
initializeMetadataInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction
];
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(authority, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions(instructions, tx)
);
// Sign transaction message with all required signers
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed", skipPreflight: true }
);
// Get transaction signature
const transactionSignature = getSignatureFromTransaction(signedTransaction);
console.log("Mint Address:", mint.address.toString());
console.log(
"Token account with Metadata + Metadata Pointer:",
tokenAccount.address.toString()
);
console.log("Transaction Signature:", transactionSignature);
Console
Click to execute the code.

Rust

use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{Keypair, Signer},
system_instruction::create_account,
transaction::Transaction,
};
use spl_token_2022::{
ID as TOKEN_2022_PROGRAM_ID,
extension::{
ExtensionType, metadata_pointer::instruction::initialize as initialize_metadata_pointer,
},
instruction::initialize_mint,
state::Mint,
};
use spl_token_metadata_interface::{
instruction::initialize as initialize_token_metadata, state::TokenMetadata,
}; // < 0.7.0
#[tokio::main]
async fn main() -> Result<()> {
// Create connection to local validator
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let latest_blockhash = client.get_latest_blockhash().await?;
// Generate a new keypair for the fee payer
let fee_payer = Keypair::new();
// Airdrop 5 SOL to fee payer
let airdrop_signature = client
.request_airdrop(&fee_payer.pubkey(), 5_000_000_000)
.await?;
client.confirm_transaction(&airdrop_signature).await?;
loop {
let confirmed = client.confirm_transaction(&airdrop_signature).await?;
if confirmed {
break;
}
}
// Generate keypair for the mint
let mint = Keypair::new();
// Token metadata
let token_metadata = TokenMetadata {
update_authority: Some(fee_payer.pubkey()).try_into().unwrap(),
mint: mint.pubkey(),
name: "OPOS".to_string(),
symbol : "OPS".to_string(),
uri : "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json".to_string(),
additional_metadata: vec![("description".to_string(),"only possible on Solana".to_string())]
};
// Calculate space for mint with metadata pointer and token metadata extensions
let mint_space =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MetadataPointer])?;
let metadata_len = token_metadata.tlv_size_of()?;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space + metadata_len)
.await?;
// Instruction to create new account for mint (token22)
let create_mint_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&mint.pubkey(), // new account (mint)
mint_rent, // lamports
mint_space as u64, // space
&TOKEN_2022_PROGRAM_ID, // program id
);
// Instruction to initialize metadata pointer (pointing to itself for self-managed metadata)
let initialize_metadata_pointer_instruction = initialize_metadata_pointer(
&TOKEN_2022_PROGRAM_ID,
&mint.pubkey(),
Some(fee_payer.pubkey()), // authority
Some(mint.pubkey()), // metadata address (pointing to self)
)?;
// Instruction to initialize mint account data
let initialize_mint_instruction = initialize_mint(
&TOKEN_2022_PROGRAM_ID, // program id
&mint.pubkey(), // mint
&fee_payer.pubkey(), // mint authority
Some(&fee_payer.pubkey()), // freeze authority
9, // decimals
)?;
// Instruction to initialize token metadata
let initialize_metadata_instruction = initialize_token_metadata(
&TOKEN_2022_PROGRAM_ID, // program id
&mint.pubkey(), //metadata
&fee_payer.pubkey(), // update authority
&mint.pubkey(), // mint
&fee_payer.pubkey(), // mint authority
token_metadata.name.to_string(), // name
token_metadata.symbol.to_string(), // symbol
token_metadata.uri.to_string(), // uri
);
// Construct transaction with all instructions
let transaction = Transaction::new_signed_with_payer(
&[
create_mint_account_instruction,
initialize_metadata_pointer_instruction,
initialize_mint_instruction,
initialize_metadata_instruction,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint],
latest_blockhash,
);
// Send and confirm transaction
let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
println!("Mint Address: {}", mint.pubkey());
println!("\nSuccessfully created mint with metadata pointer and token metadata extensions");
println!("Transaction Signature: {}", transaction_signature);
Ok(())
}
Console
Click to execute the code.

Is this page helpful?

Table des matières

Modifier la page