Typescript
Token2022 mit Web3.js

Token2022 mit Web3.js

Die Metadata-Erweiterung

Die Metadata Erweiterung ist eine Mint Kontoerweiterung, die die Möglichkeit bietet, Metadaten direkt in Mint-Konten nativ und ohne die Verwendung eines anderen Programms einzubetten.

Initialisierung des Mint-Kontos

Die Metadata Erweiterung unterscheidet sich etwas von dem, was wir gewohnt sind, da sie aus 2 verschiedenen Erweiterungen besteht, die beide auf ein Mint Konto angewendet werden:

  • Die Metadata Erweiterung, die alle Metadateninformationen wie Name, Symbol, URI und zusätzliche Konten enthält.

  • Die MetadataPointer Erweiterung, die auf das Mint Konto verweist, auf dem die Metadata Erweiterung existiert.

Normalerweise befinden sich diese beiden Erweiterungen bei Verwendung auf demselben Mint Konto; und wir werden für dieses Beispiel dasselbe tun.

Beginnen wir mit einigen Grundlagen, bevor wir in den Code eintauchen:

Während die MetadataPointer Erweiterung im @solana/spl-token Paket enthalten ist, müssen wir zur Initialisierung der Metadata das @solana/spl-token-metadata Paket verwenden.

Installieren wir also das erforderliche Paket:

text
npm i @solana/spl-token-metadata

Darüber hinaus ist die Metadata Erweiterung eine der "einzigen" Erweiterungen, die erfordert, dass du die Erweiterung initialisierst, nachdem du das Mint Konto initialisiert hast.

Dies liegt daran, dass die Metadaten-Initialisierungsanweisung dynamisch den erforderlichen Speicherplatz für den Metadateninhalt variabler Länge zuweist.

Gleichzeitig bedeutet dies, dass wir das Mint Konto mit genügend Lamports initialisieren müssen, um mit der Metadata Erweiterung mietfrei zu sein, aber nur genügend Speicherplatz für die MetadataPointer Erweiterung zuweisen, da die token_metadata_initialize() Anweisung den Speicherplatz korrekt erhöht.

Im Code sieht das so aus:

ts
import {
    Keypair,
    SystemProgram,
    Transaction,
    sendAndConfirmTransaction,
} from '@solana/web3.js';
import {
    createInitializeMintInstruction,
    TYPE_SIZE,
    LENGTH_SIZE,
    createInitializeMetadataPointerInstruction,
    getMintLen,
    ExtensionType,
    TOKEN_2022_PROGRAM_ID,
} from '@solana/spl-token';

import { 
    createInitializeInstruction, 
    pack, 
    TokenMetadata 
} from "@solana/spl-token-metadata";

const mint = Keypair.generate();

const metadata: TokenMetadata = {
    mint: mint.publicKey,
    name: "Test Token",
    symbol: "TST",
    uri: "https://example.com/metadata.json",
    additionalMetadata: [["customField", "customValue"]],
};

// Size of Mint Account with extensions
const mintLen = getMintLen([ExtensionType.MetadataPointer]);

// Size of the Metadata Extension
const metadataLen = TYPE_SIZE + LENGTH_SIZE + pack(metadata).length;

// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen + metadataLen);

const createAccountInstruction = SystemProgram.createAccount({
    fromPubkey: keypair.publicKey,
    newAccountPubkey: mint.publicKey,
    space: mintLen,
    lamports,
    programId: TOKEN_2022_PROGRAM_ID,
});

const initializeMetadataPointer = createInitializeMetadataPointerInstruction(
    mint.publicKey,
    keypair.publicKey,
    mint.publicKey,
    TOKEN_2022_PROGRAM_ID,
);

const initializeMintInstruction = createInitializeMintInstruction(
    mint.publicKey,
    6,
    keypair.publicKey,
    null,
    TOKEN_2022_PROGRAM_ID,
);

const initializeMetadataInstruction = createInitializeInstruction(
    {
        programId: TOKEN_2022_PROGRAM_ID,
        mint: mint.publicKey,
        metadata: mint.publicKey,
        name: metadata.name,
        symbol: metadata.symbol,
        uri: metadata.uri,
        mintAuthority: keypair.publicKey,
        updateAuthority: keypair.publicKey,
    }
);

const updateMetadataFieldInstructions = createUpdateFieldInstruction({
    metadata: mint.publicKey,
    updateAuthority: keypair.publicKey,
    programId: TOKEN_2022_PROGRAM_ID,
    field: metadata.additionalMetadata[0][0],
    value: metadata.additionalMetadata[0][1],
    });

const transaction = new Transaction().add(
    createAccountInstruction,
    initializeMetadataPointer,
    initializeMintInstruction,
    initializeMetadataInstruction,
    updateMetadataFieldInstructions,
);

const signature = await sendAndConfirmTransaction(connection, transaction, [keypair, mint]);

console.log(`Mint created! Check out your TX here: https://explorer.solana.com/tx/${signature}?cluster=devnet`);

Die initialize Anweisung für den metadata-interface erlaubt keine additionalMetadata. Aus diesem Grund müssen wir, wenn wir einen Asset erstellen möchten, der dies hat, die token_metadata_update_field() Anweisung verwenden, die wir im nächsten Abschnitt sehen werden.

Aktualisieren der Metadaten

Es ist möglich, alle Felder der Metadaten mit derselben Anweisung token_metadata_update_field() zu aktualisieren.

Für die additionalMetadata funktioniert dies etwas anders, da wir ein vorhandenes Feld aktualisieren können, indem wir einfach denselben Field mit einem neuen Wert übergeben oder einfach ein neues Feld zum Metadata hinzufügen.

Unter der Haube verwendet das Programm dieselbe Anweisung mit unterschiedlichen Flags, je nachdem, was wir ändern möchten. Das bedeutet, dass wir alle Felder wie folgt ändern können:

ts
const newMetadata: TokenMetadata = {
    mint: mint.publicKey,
    name: "New Name",
    symbol: "TST2",
    uri: "https://example.com/metadata2.json",
    additionalMetadata: [
        ["customField1", "customValue1"],
        ["customField2", "customValue2"],
    ],
};

// Size of Mint Account with extensions
const mintLen = getMintLen([ExtensionType.MetadataPointer]);

// Size of the Metadata Extension
const metadataLen = TYPE_SIZE + LENGTH_SIZE + pack(newMetadata).length;

// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen + metadataLen);

// Get the old balance of the keypair
const oldBalance = await connection.getBalance(mint.publicKey)

console.log(`Old balance: ${oldBalance}`);
console.log(`Lamports: ${lamports}`);

// Add lamports to the Mint if needed to cover the new metadata rent exemption
if (oldBalance < lamports) {
    const transferInstruction = SystemProgram.transfer({
        fromPubkey: keypair.publicKey,
        toPubkey: mint.publicKey,
        lamports: lamports - oldBalance,
    });

    const transaction = new Transaction().add(transferInstruction);

    const signature = await sendAndConfirmTransaction(connection, transaction, [keypair], {commitment: "finalized"});

    console.log(`Lamports added to Mint! Check out your TX here: https://explorer.solana.com/tx/${signature}?cluster=devnet`);
}

const updateMetadataNameInstructions = createUpdateFieldInstruction({
    metadata: mint.publicKey,
    updateAuthority: keypair.publicKey,
    programId: TOKEN_2022_PROGRAM_ID,
    field: "Name", // Field | string
    value: "New Name",
});

const updateMetadataSymbolInstructions = createUpdateFieldInstruction({
    metadata: mint.publicKey,
    updateAuthority: keypair.publicKey,
    programId: TOKEN_2022_PROGRAM_ID,
    field: "Symbol", // Field | string
    value: "TST2",
});

const updateMetadataUriInstructions = createUpdateFieldInstruction({
    metadata: mint.publicKey,
    updateAuthority: keypair.publicKey,
    programId: TOKEN_2022_PROGRAM_ID,
    field: "Uri", // Field | string
    value: "https://example.com/metadata2.json",
});

const updateMetadataAdditionalMetadataInstructions = createUpdateFieldInstruction({
    metadata: mint.publicKey,
    updateAuthority: keypair.publicKey,
    programId: TOKEN_2022_PROGRAM_ID,
    field: "customField2", // Field | string
    value: "customValue2",
});

const transaction = new Transaction().add(
    updateMetadataNameInstructions,
    updateMetadataSymbolInstructions,
    updateMetadataUriInstructions,
    updateMetadataAdditionalMetadataInstructions,
);

const signature = await sendAndConfirmTransaction(connection, transaction, [keypair]);

console.log(`Metadata updated! Check out your TX here: https://explorer.solana.com/tx/${signature}?cluster=devnet`);

Wie Sie sehen können, müssen wir immer sicherstellen, dass das Konto mietfrei ist. Deshalb führen wir zu Beginn alle Berechnungen zur Miete durch und überweisen bei Bedarf zusätzliche Lamports.

Wir können auch Field in der additionalMetadata Struktur mit der RemoveKey Anweisung entfernen, wie hier gezeigt:

ts
const removeMetadataKeyInstructions = createRemoveKeyInstruction({
    metadata: mint.publicKey,
    updateAuthority: keypair.publicKey,
    programId: TOKEN_2022_PROGRAM_ID,
    key: "customField", // Field | string
    idempotent: true,
});
Blueshift © 2025Commit: e573eab