हमने पिछले ट्यूटोरियल में Metaplex मेटाडेटा मानक (metadata standard) का परिचय दिया था। इस ट्यूटोरियल में, हम एक SPL टोकन बनाएंगे और Metaplex मानक का उपयोग करके इसके साथ मेटाडेटा जोड़ेंगे।
हम एक Anchor प्रोग्राम बनाएंगे जो Metaplex मानक का उपयोग करके जुड़े हुए मेटाडेटा के साथ SPL टोकन बनाता है। यह हमें अपने टोकन में नाम, प्रतीक (symbols), चित्र और अन्य विशेषताएं (attributes) जैसी जानकारी जोड़ने की अनुमति देता है।
निर्माण शुरू करने से पहले, आइए उन Metaplex मानकों को समझें जो यह निर्धारित करते हैं कि टोकन मेटाडेटा को कैसे संरचित (structured) किया जाना चाहिए।
Metaplex Token Standards और URI Formats
जब हम अपने टोकन के लिए मेटाडेटा बनाते हैं, तो हमें Metaplex द्वारा परिभाषित विशिष्ट JSON प्रारूपों (formats) का पालन करना होता है। इसकी संरचना उस टोकन के प्रकार पर निर्भर करती है जिसे हम बना रहे हैं (NFT, fungible token, आदि)।
तीन मुख्य मानक हैं:
Fungible Standard (metadata account token_standard = 2)
यह मेटाडेटा के साथ आपका नियमित SPL टोकन है। यह वह उदाहरण है जिसे हम इस लेख में बाद में बनाएंगे।
इसका मेटाडेटा JSON स्कीमा इस प्रकार परिभाषित किया गया है:
{
"name": "Example Token",
"symbol": "EXT",
"description": "A basic fungible SPL token with minimal metadata.",
"image": "https://example.com/images/ext-logo.png"
}
Fungible Asset Standard (token_standard = 1)
यह Ethereum पर ERC-1155 के समान है, जिसका उपयोग इन-गेम मुद्रा या वस्तुओं (items) के लिए किया जाता है। इसे एक fungible SPL टोकन के रूप में परिभाषित किया गया है जिसकी आपूर्ति (supply) 1 से अधिक है लेकिन शून्य दशमलव (zero decimal) के साथ (अर्थात कोई आंशिक इकाई नहीं)।
इसके JSON स्कीमा में कुछ अतिरिक्त फ़ील्ड शामिल हैं, जैसे attributes:
{
"name": "Game Sword",
"description": "A rare in-game sword used in the battle arena.",
"image": "https://example.com/images/sword.png",
"animation_url": "https://example.com/animations/sword-spin.mp4",
"external_url": "https://game.example.com/item/1234",
"attributes": [
{ "trait_type": "Damage", "value": "12" },
{ "trait_type": "Durability", "value": "50" }
],
"properties": {
"files": [
{
"uri": "https://example.com/images/sword.png",
"type": "image/png"
}
],
"category": "image"
}
}
Non-Fungible Standard (token_standard = 0)
यह Ethereum पर ERC-721 के समान है — यह एक Non-Fungible Token (NFT) का प्रतिनिधित्व करता है। हालाँकि, Solana पर, प्रत्येक NFT 1 की आपूर्ति और 0 दशमलव के साथ एक अलग मिंट (mint) है, जबकि Ethereum पर, ERC-721 एक ही कॉन्ट्रैक्ट के भीतर अद्वितीय (unique) टोकन ID का उपयोग करता है।
Non-Fungible Standard के लिए JSON स्कीमा ऊपर दिए गए Fungible Asset मानक के समान है। दोनों मानक बिल्कुल एक ही मेटाडेटा संरचना का उपयोग करते हैं — अंतर केवल ऑन-चेन (आपूर्ति और दशमलव) पर है, JSON प्रारूप में नहीं।
{
"name": "Rare Art Piece",
"description": "A one-of-one digital artwork by Artist X.",
"image": "https://example.com/images/artwork.png",
"animation_url": "https://example.com/animations/artwork-loop.mp4",
"external_url": "https://artistx.example.com/rare-art-piece",
"attributes": [
{ "trait_type": "Artist", "value": "Artist X" },
{ "trait_type": "Year", "value": "2025" }
],
"properties": {
"files": [
{
"uri": "https://example.com/images/artwork.png",
"type": "image/png"
}
],
"category": "image"
}
}
नोट: Ethereum पर, एक NFT संग्रह आमतौर पर एक ही कॉन्ट्रैक्ट में रहता है जो कई NFTs को मिंट और प्रबंधित करता है। Solana पर, प्रत्येक NFT अपना स्वयं का मिंट होता है, और संग्रह एक ही कॉन्ट्रैक्ट द्वारा नहीं, बल्कि Metaplex मेटाडेटा (collection फ़ील्ड जिसे हमने पिछले ट्यूटोरियल में कवर किया था) में एक सत्यापित (verified) ऑन-चेन लिंक द्वारा बनाए जाते हैं।
अब जब हम इन मानकों को समझ गए हैं, तो आइए मेटाडेटा के साथ एक fungible टोकन बनाने के लिए अपना प्रोग्राम बनाएं।
Fungible Standard टोकन लागू करना
यह वह है जो हम पूरा करेंगे:
- हम SPL टोकन में मेटाडेटा जोड़ने के लिए एक फ़ंक्शन के साथ एक Anchor प्रोग्राम बनाएंगे
- एक SPL टोकन (मिंट अकाउंट) बनाएंगे
- एक मेटाडेटा अकाउंट बनाने और इसे टोकन से लिंक करने के लिए CPI के माध्यम से Metaplex Token Metadata Program का उपयोग करेंगे
- टोकन URI और इसकी सामग्री (जैसे टोकन इमेज) को एक स्थायी स्टोरेज में स्टोर करेंगे, जिसे टोकन का मेटाडेटा अकाउंट संदर्भित (reference) करेगा
प्रोजेक्ट सेटअप
सबसे पहले, anchor init spl_with_metadata के साथ एक नया Anchor प्रोजेक्ट बनाएं।
फिर, हमारे प्रोजेक्ट को ठीक से कॉन्फ़िगर करने के लिए Anchor.toml फ़ाइल को इससे अपडेट करें। हम क्लस्टर को ‘devnet’ पर सेट करेंगे क्योंकि हमें वास्तविक Metaplex Token Metadata Program के साथ इंटरैक्ट करने की आवश्यकता है, जो हमारे स्थानीय वातावरण (local environment) में मौजूद नहीं है। हम SPL टोकन और मेटाडेटा के साथ काम करने के लिए आवश्यक निर्भरताएं (dependencies) भी जोड़ेंगे:
[toolchain]
package_manager = "yarn"
[features]
resolution = true
skip-lint = false
[programs.localnet]
spl_token_with_metadata = "ApCjqNHgvuvsiQYpX4kGCxXTipcWJUe7NmnNfq3UKrwD"
[registry]
url = "https://api.apr.dev"
[provider]
cluster = "devnet" # added this
wallet = "~/.config/solana/id.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
इसके बाद, programs/spl_token_with_metadata/Cargo.toml फ़ाइल को अपडेट करें।
[package]
name = "spl_token_with_metadata"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "spl_token_with_metadata"
[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] # added "anchor-spl/idl-build"
[dependencies]
anchor-lang = "0.31.0"
anchor-spl = { version = "0.31.0", features = ["token"] } # added this
mpl-token-metadata = "5.1.0" # added this
हम अपने प्रोजेक्ट को Anchor SPL और Metaplex Token Metadata क्रेट (crate) का उपयोग करने के लिए कॉन्फ़िगर करते हैं।
हम विशिष्ट उद्देश्यों के लिए ये निर्भरताएं (dependencies) जोड़ रहे हैं:
anchor-spl: Solana के SPL टोकन प्रोग्राम के लिए Anchor-संगत इंटरफेस प्रदान करता हैmpl-token-metadata: हमें अपने SPL टोकन के लिए मेटाडेटा बनाने और प्रबंधित करने के लिए Metaplex के Token Metadata Program के साथ इंटरैक्ट करने की अनुमति देता है
हमने अपने Cargo.toml में idl-build = ["anchor-spl/idl-build"] फीचर जोड़ा है ताकि एक IDL फ़ाइल जनरेट की जा सके जिसमें SPL टोकन प्रकार शामिल हों, जिससे हमारे TypeScript क्लाइंट को हमारे प्रोग्राम के साथ ठीक से इंटरैक्ट करने की अनुमति मिल सके।
Anchor प्रोग्राम कोड जोड़ें
अब Anchor प्रोग्राम को निम्नलिखित कोड के साथ अपडेट करें।
यहां हमने एक प्रदान किए गए SPL टोकन में मेटाडेटा जोड़ने के लिए एक create_token_metadata फ़ंक्शन परिभाषित किया है। जैसे-जैसे हम आगे बढ़ेंगे, हम कोड को विस्तार से समझाएंगे।
// Import the necessary dependencies for out program: Anchor, Anchor SPL and Metaplex Token Metadata crate
use anchor_lang::prelude::*;
use anchor_spl::token::Mint;
use mpl_token_metadata::instructions::{
CreateMetadataAccountV3Cpi, CreateMetadataAccountV3CpiAccounts,
CreateMetadataAccountV3InstructionArgs,
};
use mpl_token_metadata::types::{Creator, DataV2};
use mpl_token_metadata::ID as METADATA_PROGRAM_ID;
declare_id!("2SZvgGtgotJFy1aKd4Rnm7UEZNxUdP4sdXbeLDgKDiGM"); // run Anchor sync to update your program ID
#[program]
pub mod spl_token_with_metadata {
use super::*;
pub fn create_token_metadata(
ctx: Context<CreateTokenMetadata>,
name: String,
symbol: String,
uri: String,
seller_fee_basis_points: u16,
is_mutable: bool,
) -> Result<()> {
// Create metadata instruction arguments using the Fungible Standard format
// This follows the token_standard = 2 format we discussed earlier
let data = DataV2 {
name,
symbol,
uri, // Points to JSON with name, symbol, description, and image
seller_fee_basis_points,
creators: Some(vec![Creator {
address: ctx.accounts.payer.key(),
verified: true,
share: 100,
}]),
collection: None,
uses: None,
};
// Find the metadata account address (PDA)
let mint_key = ctx.accounts.mint.key();
let seeds = &[
b"metadata".as_ref(),
METADATA_PROGRAM_ID.as_ref(),
mint_key.as_ref(),
];
let (metadata_pda, _) = Pubkey::find_program_address(seeds, &METADATA_PROGRAM_ID);
// Ensure the provided metadata account matches the PDA
require!(
metadata_pda == ctx.accounts.metadata.key(),
MetaplexError::InvalidMetadataAccount
);
// Create and execute the CPI to create metadata
let token_metadata_program_info = ctx.accounts.token_metadata_program.to_account_info();
let metadata_info = ctx.accounts.metadata.to_account_info();
let mint_info = ctx.accounts.mint.to_account_info();
let authority_info = ctx.accounts.authority.to_account_info();
let payer_info = ctx.accounts.payer.to_account_info();
let system_program_info = ctx.accounts.system_program.to_account_info();
let rent_info = ctx.accounts.rent.to_account_info();
let cpi = CreateMetadataAccountV3Cpi::new(
&token_metadata_program_info,
CreateMetadataAccountV3CpiAccounts {
metadata: &metadata_info,
mint: &mint_info,
mint_authority: &authority_info,
payer: &payer_info,
update_authority: (&authority_info, true),
system_program: &system_program_info,
rent: Some(&rent_info),
},
CreateMetadataAccountV3InstructionArgs {
data,
is_mutable,
collection_details: None,
},
);
cpi.invoke()?;
Ok(())
}
}
हम अपने प्रोग्राम के लिए आवश्यक निर्भरताओं (dependencies) को आयात (import) करते हैं: टोकन संचालन के लिए anchor_spl क्रेट से SPL टोकन उपयोगिताएँ (utilities) और मेटाडेटा अकाउंट बनाने और संरचित करने के लिए mpl_token_metadata क्रेट से Metaplex टोकन मेटाडेटा घटक।
create_token_metadata फ़ंक्शन एक SPL टोकन मिंट में मेटाडेटा जोड़ता है। हम बाद में परीक्षण (testing) के दौरान बनाए गए एक मिंट एड्रेस के साथ इसे कॉल करके इसका प्रदर्शन करेंगे।
create_token_metadata फ़ंक्शन की व्याख्या
आइए create_token_metadata फ़ंक्शन को समझते हैं, जिसकी शुरुआत सबसे महत्वपूर्ण भाग से करते हैं:
मेटाडेटा संरचना को परिभाषित करना
सबसे पहले हम DataV2 स्ट्रक्चर (struct) प्रकार के साथ मेटाडेटा अकाउंट की सामग्री को परिभाषित करते हैं जिसे mpl_token_metadata::types (Metaplex Metadata क्रेट) से आयात किया गया था।

यह संरचना हमारे मेटाडेटा अकाउंट के लिए डेटा रखती है और मेटाडेटा अकाउंट बनाते समय Metaplex द्वारा उपयोग की जाती है।
मेटाडेटा अकाउंट को वैलिडेट करना
इसके बाद, हम यह सुनिश्चित करने के लिए एक जांच करते हैं कि टोकन (मिंट) के लिए सही मेटाडेटा अकाउंट पास किया गया है।

CPI के माध्यम से मेटाडेटा अकाउंट बनाना
अंत में, हम CPI के माध्यम से Metaplex Token Metadata Program के लिए एक CreateMetadataAccountV3 निर्देश (instruction) का निर्माण और निष्पादन (execute) करते हैं (हमने पिछले लेख में इस पर चर्चा की थी)।

ऊपर दी गई छवि (image) से, हम देख सकते हैं कि हम CreateMetadataAccountV3Cpi में कई अकाउंट पास करते हैं। ये मेटाडेटा बनाने के लिए आवश्यक अकाउंट हैं। पास किए गए इन अकाउंट्स को नीचे दिए गए कॉन्टेक्स्ट स्ट्रक्चर (context struct) में परिभाषित किया गया है जहां हम प्रत्येक अकाउंट के स्रोत और उद्देश्य की व्याख्या करते हैं।
अब, आइए create_token_metadata फ़ंक्शन के लिए कॉन्टेक्स्ट स्ट्रक्चर को देखें, इसमें CreateMetadataAccountV3Cpi CPI कॉल के लिए आवश्यक अकाउंट शामिल हैं:
नीचे दिए गए CreateTokenMetadata अकाउंट स्ट्रक्चर में शामिल हैं:
metadata: मेटाडेटा PDA जिसे Metaplex Token Metadata Program द्वारा बनाया जाएगाmint: एक मौजूदा SPL टोकन मिंट अकाउंट जिसमें हम मेटाडेटा जोड़ेंगे। अपने प्रोग्राम का परीक्षण करते समय हम इसे बनाएंगे और पास करेंगेauthority: इस लेनदेन (transaction) को अधिकृत (authorize) करने के लिए मिंट अथॉरिटीpayer: वॉलेट अकाउंट जो लेनदेन शुल्क (transaction fees) और रेंट का भुगतान करता हैsystem_program: नए अकाउंट बनाने के लिए Solana System Programrent: रेंट छूट (rent exemption) की गणना के लिए Solana Rent sysvartoken_metadata_program: ऑन-चेन Metaplex Token Metadata Program (हमारे कोड मेंMETADATA_PROGRAM_IDके रूप में परिभाषित निश्चित पते के साथ)
हम वैलिडेशन विफलताओं (validation failures) को संभालने के लिए एक कस्टम MetaplexError एरर (error) भी परिभाषित करते हैं। इस कोड को प्रोग्राम कोड में जोड़ें।
#[derive(Accounts)]
pub struct CreateTokenMetadata<'info> {
/// CHECK: metadata PDA (will be created by the Metaplex Token Metadata program via CPI in the create_token_metadata function)
#[account(mut)]
pub metadata: AccountInfo<'info>,
// The mint account of the token
#[account(mut)]
pub mint: Account<'info, Mint>,
// The mint authority of the token
pub authority: Signer<'info>,
// The account paying for the transaction
#[account(mut)]
pub payer: Signer<'info>,
// Onchain programs our code depends on
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
/// CHECK: This is the Metaplex Token Metadata program
#[account(address = METADATA_PROGRAM_ID)]
// constraint to ensure the right account is passed
pub token_metadata_program: AccountInfo<'info>,
}
#[error_code]
pub enum MetaplexError {
#[msg("The provided metadata account does not match the PDA for this mint")]
InvalidMetadataAccount,
}
आइए अपने प्रोग्राम के लिए इस परीक्षण (test) को लागू करें।
हमारे प्रोग्राम की टेस्टिंग
Irys को समझना
Irys (पूर्व में Bundlr) एक ऐसी सेवा है जो Arweave, एक स्थायी विकेंद्रीकृत (decentralized) स्टोरेज में डेटा अपलोड करना आसान बनाती है। अपने परीक्षण में, हम अपने टोकन की छवि और मेटाडेटा JSON दोनों को स्थायी स्टोरेज में अपलोड करने के लिए Irys का उपयोग करेंगे।
Irys के बारे में मुख्य बिंदु:
- यह
irysStorageमॉड्यूल के माध्यम से@metaplex-foundation/jsपैकेज में पहले से ही शामिल है, इसलिए@metaplex-foundation/jsस्थापित करने के बाद किसी अतिरिक्त निर्भरता इंस्टॉलेशन (dependency installation) की आवश्यकता नहीं है। आप इसे आसानी से इंपोर्ट (import) कर सकते हैं। - कोई अलग अकाउंट बनाने की आवश्यकता नहीं है क्योंकि यह भुगतान के लिए हमारे मौजूदा Solana वॉलेट का उपयोग करता है
- देवनेट (devnet) में, आपके वॉलेट से देवनेट SOL का उपयोग करके अपलोड के लिए भुगतान किया जाता है (हम बाद में एयरड्रॉप (airdrop) के माध्यम से देवनेट SOL का अनुरोध करेंगे)
अब नीचे दिए गए कोड के साथ tests/spl_token_with_metadata.ts में प्रोग्राम टेस्ट को अपडेट करें।
परीक्षण (test) निम्नलिखित कार्य करता है:
- यह Metaplex Token Metadata Program ID को कॉन्सटेंट (constant) के रूप में परिभाषित करता है (PDA व्युत्पत्ति (derivation) और CPI कॉल के लिए आवश्यक)
- जेनरेट किए गए कीपेयर अकाउंट (keypair account) का उपयोग करके एक SPL टोकन बनाता है
- स्थानीय रूप से (locally) संग्रहीत छवि (image) को Arweave देवनेट पर अपलोड करने के लिए Metaplex को Irys के साथ सेट अप करता है, जो अपलोड की गई छवि का URI लौटाता है
- परीक्षण में JSON मेटाडेटा बनाता है (नाम, प्रतीक, विवरण और छवि URI के साथ) और इसे Arweave पर अपलोड करता है
- ‘metadata’ प्रीफिक्स (prefix), मेटाडेटा प्रोग्राम ID, और मिंट एड्रेस को सीड (seeds) के रूप में उपयोग करके मेटाडेटा PDA प्राप्त करता है
- हमारे Anchor प्रोग्राम में
create_token_metadataको कॉल करता है - अंत में, यह सत्यापित (verify) करता है कि मेटाडेटा अकाउंट मौजूद है और उसका मालिक (owner) सही है
प्रत्येक चरण को दिखाने के लिए कोड टिप्पणियाँ (comments) जोड़ी गई हैं।
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import {
Metaplex,
irysStorage,
keypairIdentity,
toMetaplexFile,
} from "@metaplex-foundation/js";
import { createMint } from "@solana/spl-token";
import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js";
import { assert } from "chai";
import { readFileSync } from "fs";
import path from "path";
import { SplTokenWithMetadata } from "../target/types/spl_token_with_metadata";
describe("spl_token_with_metadata", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace
.splTokenWithMetadata as Program<SplTokenWithMetadata>;
const wallet = provider.wallet as anchor.Wallet;
const TOKEN_METADATA_PROGRAM_ID = new PublicKey(
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
);
// configure Metaplex with Irys (formerly Bundlr)
const metaplex = Metaplex.make(provider.connection)
.use(keypairIdentity(wallet.payer))
.use(
irysStorage({
address: "https://devnet.irys.xyz", // Irys endpoint
providerUrl: provider.connection.rpcEndpoint,
timeout: 60_000,
})
);
it("creates token with metadata", async () => {
// Create the mint
const mintKeypair = Keypair.generate();
await createMint(
provider.connection,
wallet.payer,
wallet.publicKey,
wallet.publicKey,
9,
mintKeypair
);
const mintPubkey = mintKeypair.publicKey;
console.log("Mint Pubkey:", mintPubkey.toBase58());
// Read & convert our image into a MetaplexFile
const imageBuffer = readFileSync(
path.resolve(__dirname, "../assets/image/kitten.png")
);
const metaplexFile = toMetaplexFile(imageBuffer, "kitten.png");
// Upload image, get arweave URI string
const arweaveImageUri: string = await metaplex.storage().upload(metaplexFile);
const imageTxId = arweaveImageUri.split("/").pop()!;
const imageUri = `https://devnet.irys.xyz/${imageTxId}`;
console.log("Devnet Irys image URL:", imageUri); // using Irys devnet gateway because Arweave public gateway has no devnet
// Build our JSON metadata object following the Fungible Standard format
// This matches the token_standard = 2 format we explained earlier
const metadata = {
name: "Test Token",
symbol: "TEST",
description: "Test token with metadata example",
image: imageUri,
};
// Upload JSON, get arweave URI string
const arweaveMetadataUri: string = await metaplex
.storage()
.uploadJson(metadata);
const metadataTxId = arweaveMetadataUri.split("/").pop()!;
const metadataUri = `https://devnet.irys.xyz/${metadataTxId}`;
console.log("Devnet Irys metadata URL:", metadataUri); // using Irys devnet gateway because Arweave public gateway has no devnet
// Derive on-chain metadata PDA
const [metadataPda] = PublicKey.findProgramAddressSync(
[
Buffer.from("metadata"),
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
mintPubkey.toBuffer(),
],
TOKEN_METADATA_PROGRAM_ID
);
console.log("Metadata PDA:", metadataPda.toBase58());
// Call the create_token_metadata function
const tx = await program.methods
.createTokenMetadata(
metadata.name,
metadata.symbol,
metadataUri,
100, // 1%
true // isMutable
)
.accounts({
metadata: metadataPda,
mint: mintPubkey,
authority: wallet.publicKey,
payer: wallet.publicKey,
systemProgram: SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
})
.rpc();
console.log("Transaction signature:", tx);
// Assert the account exists & is owned by the Metadata program
const info = await provider.connection.getAccountInfo(metadataPda);
assert(info !== null, "Metadata account must exist");
assert(
info.owner.equals(TOKEN_METADATA_PROGRAM_ID),
"Wrong owner for metadata account"
);
});
});
अब जब हमारा परीक्षण तैयार है, तो उस छवि को रखने के लिए हमारे वर्कस्पेस में एक assets/image निर्देशिका (directory) बनाएं जिसे हम अपने टोकन छवि के रूप में उपयोग करेंगे। इस छवि का उपयोग Irys द्वारा हमारे परीक्षण में यहां किया जाएगा:

हमने यह छवि आपके लिए पहले ही Irys देवनेट पर अपलोड कर दी है। इसे डाउनलोड करने के लिए नीचे दिए गए लिंक पर क्लिक करें और इसे उस निर्देशिका में रखें जिसे आपने अभी बनाया है: https://devnet.irys.xyz/8VY89xG1RiUjtz1Lwgip7eUxZvtsdkf1gViGYaDKmwx8
अब, परीक्षण के लिए निर्भरताएँ (dependencies) स्थापित करने के लिए टर्मिनल पर npm install @solana/spl-token @metaplex-foundation/js चलाएँ।
इसके बाद, देवनेट का उपयोग करने के लिए Solana को कॉन्फ़िगर करें।
टर्मिनल पर इस कमांड को चलाएँ: solana config set --url [https://api.devnet.solana.com](https://api.devnet.solana.com/)

एयरड्रॉप का अनुरोध करें: solana airdrop 5। हमें अपने प्रोग्राम और इसमें शामिल सभी अकाउंट्स को देवनेट पर परिनियोजित (deploy) करने के लिए फंड की आवश्यकता है।

अब प्रोजेक्ट बनाएं (build) और परीक्षण (test) चलाएं। यह प्रोग्राम, टोकन और मेटाडेटा अकाउंट को Solana देवनेट पर परिनियोजित कर देगा (नोट: आपके नेटवर्क कनेक्शन के आधार पर इसमें कुछ समय लग सकता है)।

हम देख सकते हैं कि मिंट (टोकन) मेटाडेटा के साथ बन गया है (और इसमें एक छवि है), जैसा कि नीचे दिखाया गया है।

हम इसका मेटाडेटा भी देख सकते हैं।

इस विशेष के लिए डिप्लॉयमेंट लिंक: https://explorer.solana.com/tx/2c27FRN48fHzzLTA9kV2XXwCEUPEQcWXvfT3k31PhPoEyFNe3bepJ7XxvKwAXekzPaV5nQeCR8mfxAeKqG15QT4Q?cluster=devnet
मेटाडेटा अकाउंट: https://explorer.solana.com/address/5feQdhNd3PxPJ9apKUpCWfB47cQdLitNMrVP8Gnq3cad?cluster=devnet
मेटाडेटा अकाउंट का प्रभाव देखने के लिए, एक नियमित SPL टोकन की डिप्लॉयमेंट (जिसका हमने पिछले ट्यूटोरियल में उपयोग किया था) के लिए UI देखें। ध्यान दें कि कैसे शीर्ष पर कोई नाम या छवि नहीं है, और History, Transfer, और Instructions टैब के बगल में Metadata के लिए कोई टैब नहीं है।

हमने सफलतापूर्वक एक SPL टोकन डिप्लॉय किया है और Metaplex मानक का उपयोग करके इसके साथ मेटाडेटा जोड़ा है, जो Fungible Standard प्रारूप का पालन करता है जिस पर हमने पहले बुनियादी नाम, प्रतीक, विवरण और छवि फ़ील्ड के साथ चर्चा की थी।
निष्कर्ष (Conclusion)
इस ट्यूटोरियल में, हमने एक SPL टोकन बनाया और Metaplex Token Metadata मानक की मदद से इसमें मेटाडेटा जोड़ा।
हमारे Anchor प्रोग्राम में, हमने टोकन के मेटाडेटा को परिभाषित करने के लिए DataV2 स्ट्रक्चर का उपयोग किया और मेटाडेटा अकाउंट बनाने के लिए mpl-token-metadata क्रेट से CreateMetadataAccountV3 निर्देश को (CPI के माध्यम से) लागू (invoke) किया। हमने टोकन की छवि और मेटाडेटा JSON को Arweave पर अपलोड करने के लिए Irys के साथ Metaplex का उपयोग किया। इसके बाद हमने पुष्टि की कि निर्माण के बाद मेटाडेटा अकाउंट मौजूद है और इसके स्वामित्व (ownership) में Metaplex प्रोग्राम है। अंत में, हमने Metaplex टोकन मानकों — Fungible (2), Fungible Asset (1), और Non-Fungible (0) — की व्याख्या की और उनके JSON URI प्रारूपों को रेखांकित किया।
यह लेख Solana पर ट्यूटोरियल सीरीज़ का हिस्सा है।