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
MetadataErweiterung, die alle Metadateninformationen wie Name, Symbol, URI und zusätzliche Konten enthält.Die
MetadataPointerErweiterung, die auf dasMintKonto verweist, auf dem dieMetadataErweiterung 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:
npm i @solana/spl-token-metadataDarü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:
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`);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:
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`);Wir können auch Field in der additionalMetadata Struktur mit der RemoveKey Anweisung entfernen, wie hier gezeigt:
const removeMetadataKeyInstructions = createRemoveKeyInstruction({
metadata: mint.publicKey,
updateAuthority: keypair.publicKey,
programId: TOKEN_2022_PROGRAM_ID,
key: "customField", // Field | string
idempotent: true,
});