# Minting avec thirdweb

[thirdweb](https://thirdweb.com/) est une plateforme qui propose des outils et une infrastructure pour créer des applications décentralisées sur plusieurs réseaux blockchain. Elle simplifie l’intégration de la blockchain grâce à des SDK, des API et des composants préconstruits.

Dans le cas du minting de NFT, thirdweb propose un tableau de bord soigné et un téléversement IPFS, ainsi que ses contrats audités et ses SDK complets.

## Minting manuel d’un seul NFT

Si vous voulez simplement téléverser une seule image et la minter, thirdweb propose une interface de déploiement en un clic.

Commencez par créer un compte thirdweb en utilisant votre portefeuille Web3 comme propriétaire. Cela vous donne accès à votre tableau de bord thirdweb.

À partir de là, vous pouvez choisir entre deux voies :

* Utilisez leur [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) page et son bouton « Deploy Now ».
* Ou utilisez l’interface « Create Token » de leur tableau de bord.

Explorons rapidement chacune d’elles sur cette page.\
Le [portail développeur thirdweb](https://portal.thirdweb.com/) vous donnera plus d’informations si nécessaire.

### Utilisation de la page TokenERC721

1. Allez à la [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) page. Celle-ci s’appuie sur leur contrat pour créer une collection de NFT.
2. Cliquez sur « Deploy Now » pour accéder à la page de déploiement du contrat.

<figure><img src="/files/27cfc412bb05bfb7cf929fa1cd98fd02d0b5c5cd" alt=""><figcaption></figcaption></figure>

3. Remplissez la section « Contract metadata » :
   1. Téléversez l’image que vous souhaitez utiliser pour représenter votre collection de NFT (ou simplement l’image que vous souhaitez minter).
   2. Donnez-lui un nom unique ainsi qu’un symbole/ticker pour votre collection.
   3. Ajoutez une description.
4. thirdweb renseigne automatiquement les adresses des destinataires pour « Primary Sales » et « Royalties » ; assurez-vous de vérifier que les adresses Web3 correspondent bien à celles de votre portefeuille.
5. Dans les « Deploy Options », choisissez Chiliz Chain (ou Chiliz Spicy Testnet si ce n’est qu’un test) puis cliquez sur « Deploy Now » :

<figure><img src="/files/b221f3df45896dd1499ba7bd381b7ea84d9ac249" alt=""><figcaption></figcaption></figure>

6. thirdweb commencera à déployer le contrat, déclenchant une fenêtre de confirmation depuis votre portefeuille Web3. Confirmez pour terminer le déploiement.\
   ![](/files/8b1218a88abe7b8ccc9b1ad5467533c473293284)
7. Une fois terminé, cliquez sur le bouton « View Contract ». Vous serez redirigé vers la page du contrat sur votre tableau de bord thirdweb, avec une liste de vérification/barre de progression :\
   ![](/files/e57209da98d66b8b977943bf207cb47677493b9c)
8. Comme vous pouvez le voir dans la liste de vérification, pour l’instant tout ce que vous avez fait est déployer un contrat ; vous devez encore effectivement minter le NFT. Dans la colonne de gauche, cliquez sur l’élément de menu « NFTs » pour ouvrir une nouvelle page, qui est pour l’instant vide :\
   ![](/files/1560da451d6083226343f50a1073aebdf6541610)
9. Cliquez sur le bouton « Mint » et un panneau latéral s’ouvre avec un champ pour chaque attribut de votre NFT. C’est ici que vous créez les métadonnées de votre NFT :\
   ![](/files/e289450f88252aa2b5ca9c71b403f43533b38db9)
10. Remplissez les différents champs. Vous pouvez créer des champs spécifiques à votre collection de NFT, comme « Artist\_name » ou « Type\_of\_work », et explorer les options avancées, mais pour ce test vous pouvez rester minimal : nom, fichier média, description.
11. Cliquez sur le bouton « Mint NFT » en bas du panneau latéral, puis approuvez la transaction depuis votre portefeuille.\
    ![](/files/f1bd3e774fe1fa7f59198ae9f74052a2dbf4096c)
12. thirdweb affichera alors la page « NFTs », avec votre NFT désormais visible, rattaché à votre contrat NFT.

TERMINE ! Vous avez minté votre NFT !

Pour vérifier qu’il est bien sur Chiliz Chain, ouvrez [Chiliscan](https://www.chiliscan.com/) (ou [sa version Testnet](https://testnet.chiliscan.com/) si vous utilisez Spicy Testnet) et copiez-collez le hash du NFT dans le champ de recherche. Il devrait immédiatement confirmer que votre contrat est bien sur la chaîne, et son onglet « Inventory » devrait lister votre NFT avec vous (ou au moins l’ID de votre portefeuille) comme propriétaire.

<figure><img src="/files/792fec1bb6640e69623af33d31af9d393ca3db3b" alt=""><figcaption></figcaption></figure>

Si vous avez minté votre NFT sur le mainnet de Chiliz Chain, vous devriez le voir apparaître dans votre compte sur des places de marché comme [Rarible](https://rarible.com/) ou [OKX](https://www.okx.com/), et à partir de là, vous pouvez commencer à le vendre !

### Utilisation du bouton « Create Token »

C’est en fait une méthode plus simple que celle ci-dessus, et c’est un ajout récent à thirdweb.

1. Depuis n’importe quel dossier de projet dans votre tableau de bord thirdweb, cliquez sur l’option « Tokens » dans la barre latérale gauche :\
   ![](/files/26f89f508a9e382890c9267212818e82c9e019a4)
2. Dans la page « Tokens » qui s’ouvre, cliquez sur le bouton « Create Token » à droite. Une fenêtre modale s’ouvre avec 2 options ; choisissez « Create NFT Collection ».\
   ![](/files/c361e54b093a9eea92fe1d78431233d78cba3dbb)
3. Cela vous amènera à une version étape par étape de la [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) page de déploiement (en pratique, cela déploiera un contrat ERC-721 Drop). Remplissez tous les champs nécessaires pour ce contrat, puis appuyez sur le bouton « Next ».
4. L’écran suivant concerne le NFT lui-même : téléversez l’image et remplissez les champs décrivant votre image, puis appuyez sur le bouton « Next ».
5. thirdweb affiche maintenant un résumé de la collection de NFT que vous êtes sur le point de lancer. Vérifiez que tout est correct puis cliquez sur le bouton « Launch NFT Collection ».
6. thirdweb prend en charge le déploiement du contrat, le minting du NFT et la définition des conditions, le tout en une seule fois. Vous devrez confirmer trois transactions via votre portefeuille Web3.\
   ![](/files/c66d1e37e6042f03217e0f5c94eb72d0c82c8746)
7. Une fois cela fait, cliquez sur le bouton « View NFT », et vous reviendrez à votre tableau de bord thirdweb, affichant la page spécifique à ce contrat — ainsi que ses NFT rattachés.

De là, vous pouvez vérifier qu’il est bien sur la chaîne via un explorateur de blocs, ou commencer à vendre votre NFT sur une place de marché.

## Minting programmatique d’un seul NFT

Voici un exemple de code qui utilise le [SDK thirdweb v5](https://portal.thirdweb.com/references/typescript/v5), qui téléverse le fichier média vers IPFS, génère le `metadata.json` fichier, puis le téléverse aussi vers IPFS.

Cela nécessitera que vous ayez :

* Un contrat ERC-721 déjà déployé sur Chiliz Chain. Dans ce cas, vous devriez le faire avec thirdweb plutôt qu’avec un autre outil.
* Une clé API thirdweb, liée au contrat ERC-721 que vous avez déployé via thirdweb. Vous pouvez la trouver sur la page du projet du contrat intelligent, sous le nom « Client ID ».
* Une installation locale du SDK thirdweb. Vous pouvez le faire avec `npm`:

```bash
npm install thirdweb dotenv
```

Le `dotenv` paramètre génère un `.env` fichier, nécessaire pour stocker des valeurs privées :

```
THIRDWEB_SECRET_KEY=YOUR_TW_SECRET_KEY # Depuis la page de votre projet thirdweb.
PRIVATE_KEY=0xabc...                   # Le portefeuille Web3 qui enverra la transaction.
CONTRACT_ADDRESS=0xYour721Address      # Votre contrat TokenERC721 déployé.
CHAIN_ID=88882                         # 88888=Mainnet, 88882=Spicy
IMAGE_PATH=./art/image.png             # Le chemin local vers votre fichier média.
NAME=Mon NFT Chiliz
DESCRIPTION=Minté sur Chiliz Chain
```

{% hint style="danger" %}
N’exposez jamais votre `PRIVATE_KEY` ni `THIRDWEB_SECRET_KEY` dans du code côté client !\
Conservez ce fichier sur un serveur/CI sécurisé, et ne le téléversez pas dans un dépôt public.
{% endhint %}

Maintenant que tout est prêt, vous pouvez vous inspirer de ce script, qui va :

1. Téléverser le fichier média vers IPFS
2. Construire et téléverser le `metadata.json` fichier
3. Minter le NFT en utilisant la méthode mintTo du contrat.
4. Afficher le `tokenId` et le `tokenUrl` obtenus.

{% hint style="success" %}
Ceci n’est qu’un exemple de code ; vous devrez l’adapter au code de votre propre projet !
{% endhint %}

{% code overflow="wrap" lineNumbers="true" fullWidth="true" %}

```typescript
import 'dotenv/config';
import fs from 'fs';
import path from 'path';

import { createThirdwebClient, getContract, sendTransaction, waitForReceipt, parseEventLogs } from "thirdweb";
import { defineChain } from "thirdweb/chains";
import { privateKeyToAccount } from "thirdweb/wallets";
import { mintTo, transferEvent, getNFT } from "thirdweb/extensions/erc721";
import { upload } from "thirdweb/storage";

function guessMime(p: string) {
  const ext = path.extname(p).toLowerCase();
  if (ext === ".png") return "image/png";
  if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
  if (ext === ".webp") return "image/webp";
  if (ext === ".gif") return "image/gif";
  if (ext === ".mp4") return "video/mp4";
  if (ext === ".webm") return "video/webm";
  return "application/octet-stream";
}

async function main() {
  const client = createThirdwebClient({ secretKey: process.env.THIRDWEB_SECRET_KEY! });
  const chain = defineChain(Number(process.env.CHAIN_ID)); // 88888 ou 88882
  const account = privateKeyToAccount({ client, privateKey: process.env.PRIVATE_KEY! });

  const contract = getContract({
    client,
    chain,
    address: process.env.CONTRACT_ADDRESS as `0x${string}`,
  });

  // Téléverser le fichier média vers IPFS
  const filePath = process.env.IMAGE_PATH!;
  const buffer = fs.readFileSync(filePath);
  const file = new File([buffer], path.basename(filePath), { type: guessMime(filePath) });

  const [imageUri] = await upload({ client, files: [file] }); // renvoie ipfs://... 
  // console.log("imageUri:", imageUri);

  // Construire et téléverser metadata.json en référençant imageUri sur IPFS
  const metadata = {
    name: process.env.NAME!,
    description: process.env.DESCRIPTION!,
    image: imageUri
  };
  const metaBlob = new Blob([JSON.stringify(metadata, null, 2)], { type: "application/json" });
  const metaFile = new File([metaBlob], "metadata.json", { type: "application/json" });

  const [metadataUri] = await upload({ client, files: [metaFile] });
  // console.log("metadataUri:", metadataUri);

  // Préparer et envoyer la transaction mintTo en utilisant l’URI des métadonnées
  // mintTo accepte une chaîne à utiliser directement comme tokenURI.
  const tx = mintTo({
    contract,
    to: account.address,
    nft: metadataUri, // Le tokenURI
  });

  const { transactionHash } = await sendTransaction({ transaction: tx, account });
  //console.log("tx:", transactionHash);

  const receipt = await waitForReceipt({ client, chain, transactionHash });

  // Analyser l’événement Transfer pour afficher tokenId
  const events = parseEventLogs({ logs: receipt.logs, events: [transferEvent()] });
  const minted = events.find(e => e.eventName === "Transfer" && e.args.from === "0x0000000000000000000000000000000000000000");
  const tokenId = minted ? (minted.args.tokenId as bigint) : undefined;
  //console.log("tokenId:", tokenId?.toString() ?? "(introuvable)");

  if (tokenId !== undefined) {
    const nft = await getNFT({ contract, tokenId });
    //console.log("tokenURI (relecture) :", nft.tokenURI);
  }
}

main().catch((err) => (console.error(err), process.exit(1)));
```

{% endcode %}

Les points importants dont vous pouvez vous inspirer sont :

* `upload` stocke les fichiers (et le JSON) sur IPFS et renvoie des `ipfs://…` URI.
* `mintTo` accepte une chaîne à utiliser directement comme `tokenURI`.
* Nous analysons l’événement standard ERC-721 `Transfer` pour lire le `tokenId`.

## Minting programmatique d’une collection de NFT

Le minting d’une collection de NFT n’est pas très différent du minting d’un seul NFT. La différence la plus notable est que votre `.env` fichier pointe vers le dossier qui contient toutes les images en utilisant `IMAGE_DIR`, plutôt que vers une seule image avec `IMAGE_PATH`.

```dotenv
THIRDWEB_SECRET_KEY=YOUR_TW_SECRET_KEY # Depuis la page de votre projet thirdweb.
PRIVATE_KEY=0xabc...                   # Le portefeuille Web3 qui enverra la transaction.
CONTRACT_ADDRESS=0xYour721Address      # Votre contrat TokenERC721 déployé.
CHAIN_ID=88882                         # 88888=Mainnet, 88882=Spicy
IMAGE_PATH=./art                       # Le chemin local vers vos fichiers média.
NAME_PREFIX=Mon NFT Chiliz              # Cela deviendra : "Mon NFT Chiliz <n>"
DESCRIPTION=Minté sur Chiliz Chain
```

L’exemple de code que nous présentons ici tient donc compte de cela :

{% code overflow="wrap" lineNumbers="true" fullWidth="true" %}

```typescript
import 'dotenv/config';
import fs from 'fs';
import path from 'path';

import { createThirdwebClient, getContract, sendTransaction, waitForReceipt, parseEventLogs } from "thirdweb";
import { defineChain } from "thirdweb/chains";
import { privateKeyToAccount } from "thirdweb/wallets";
import { mintTo, transferEvent, getNFT } from "thirdweb/extensions/erc721";
import { upload } from "thirdweb/storage";

function guessMime(p: string) {
  const ext = path.extname(p).toLowerCase();
  if (ext === ".png") return "image/png";
  if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
  if (ext === ".webp") return "image/webp";
  if (ext === ".gif") return "image/gif";
  if (ext === ".mp4") return "video/mp4";
  if (ext === ".webm") return "video/webm";
  return "application/octet-stream";
}

// Une façon de rassembler tous les fichiers
function listMediaFiles(dir: string) {
  const allow = new Set([".png", ".jpg", ".jpeg", ".webp", ".gif", ".mp4", ".webm", ".svg"]);
  return fs
    .readdirSync(dir)
    .filter((f) => allow.has(path.extname(f).toLowerCase()))
    .map((f) => path.join(dir, f));
}

async function main() {
  const client = createThirdwebClient({ secretKey: process.env.THIRDWEB_SECRET_KEY! });
  const chain = defineChain(Number(process.env.CHAIN_ID)); // 88888 ou 88882
  const account = privateKeyToAccount({ client, privateKey: process.env.PRIVATE_KEY! }); 

  const contract = getContract({
    client,
    chain,
    address: process.env.CONTRACT_ADDRESS as `0x${string}`,
  });

  // Téléverser les fichiers média d’un dossier vers IPFS
  const dirPath = process.env.IMAGES_DIR!;
  const filePaths = listMediaFiles(dirPath);
  if (!filePaths.length) {
    throw new Error(`Aucun fichier média trouvé dans ${dirPath}`);
  }

  const fileObjs = filePaths.map((p) => {
    const buffer = fs.readFileSync(p);
    return new File([buffer], path.basename(p), { type: guessMime(p) });
  });

  const imageUris = await upload({ client, files: fileObjs }); // renvoie ["ipfs://...", ...]
  //console.log("Images téléversées vers IPFS :", imageUris.length, "fichiers");

  // Construire et téléverser chaque metadata.json en référençant son imageUri
  // Utilise les variables d’environnement NAME et DESCRIPTION ; les noms deviennent "<NAME> #<index>"
  const namePrefix = process.env.NAME!;
  const description = process.env.DESCRIPTION!;

  // Créer un tableau d’objets File pour les JSON de métadonnées
  const metaFiles = imageUris.map((imageUri, i) => {
    const metadata = {
      name: `${namePrefix} #${i + 1}`,
      description,
      image: imageUri
    };
    const metaBlob = new Blob([JSON.stringify(metadata, null, 2)], { type: "application/json" });
    return new File([metaBlob], `metadata-${i + 1}.json`, { type: "application/json" });
  });

  const metadataUris = await upload({ client, files: metaFiles }); // ["ipfs://.../metadata-1.json", ...]
  //console.log("Fichiers de métadonnées téléversés vers IPFS :", metadataUris.length);

  // Préparer et envoyer les transactions mintTo en utilisant les URI des métadonnées (séquentiellement)
  const ZERO = "0x0000000000000000000000000000000000000000";

  for (let i = 0; i < metadataUris.length; i++) {
    const metadataUri = metadataUris[i];

    const tx = mintTo({
      contract,
      to: account.address,
      nft: metadataUri, // Le tokenURI
    });

    const { transactionHash } = await sendTransaction({ transaction: tx, account });
    console.log(`[${i + 1}/${metadataUris.length}] tx :`, transactionHash);

    const receipt = await waitForReceipt({ client, chain, transactionHash });

    // Analyser l’événement Transfer pour obtenir le tokenId de ce mint
    const events = parseEventLogs({ logs: receipt.logs, events: [transferEvent()] });
    const minted = events.find(
      (e) => e.eventName === "Transfer" && typeof e.args.from === "string" && e.args.from.toLowerCase() === ZERO
    );
    const tokenId = minted ? (minted.args.tokenId as bigint) : undefined;

    // console.log(`[${i + 1}/${metadataUris.length}] tokenId :`, tokenId?.toString() ?? "(introuvable)");
    if (tokenId !== undefined) {
      const nft = await getNFT({ contract, tokenId });
      // console.log(`[${i + 1}/${metadataUris.length}] tokenURI (relecture) :`, nft.tokenURI);
    }
  }
}

main().catch((err) => (console.error(err), process.exit(1)));

```

{% endcode %}

Les changements détaillés sont :

* `IMAGES_DIR` remplace `IMAGE_PATH`.
* Ajouté `listMediaFiles` pour rassembler plusieurs entrées.
* Par lots `upload` pour les images et pour les métadonnées (conserve l’ordre).
* Boucle pour `mintTo` chaque `metadataUri`, en analysant chaque `Transfer` événement pour son `tokenId`.
* Les noms sont numérotés automatiquement : `NAME #1`, `NAME #2`, … en utilisant votre `NAME`/`DESCRIPTION`.

Encore une fois, veuillez utiliser ceci comme source d’inspiration pour votre propre code, ne l’utilisez pas tel quel !

Note : si vous préférez faire du lazy minting, le contrat ERC-721 de thirdweb a [une `lazyMint` méthode](https://portal.thirdweb.com/references/typescript/v5/erc721/lazyMint).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.chiliz.com/fr-ai/develop/advanced/work-with-nfts/minting-with-thirdweb.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
