> For the complete documentation index, see [llms.txt](https://docs.chiliz.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.chiliz.com/fr/developper/notions-avancees/travailler-avec-les-nfts/minter-avec-thirdweb.md).

# Minter avec thirdweb

[thirdweb](https://thirdweb.com/) est une plateforme qui propose des outils et une infrastructure pour construire des applications décentralisées sur plusieurs réseaux blockchain. Elle simplifie l'intégration blockchain avec des SDKs, des APIs et des composants préconstruits.

Dans le cas du minting de NFTs, thirdweb offre un tableau de bord élégant et un téléversement IPFS intégré, accompagnés de ses contrats audités et de ses SDKs complets.

## Minter un seul NFT manuellement

Si vous souhaitez 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 approches :

* Utiliser leur page [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) et son bouton "Deploy Now".
* Ou utiliser l'interface "Create Token" de leur tableau de bord.

Explorons brièvement chacune d'elles sur cette page.\
Le [Portail Développeur thirdweb](https://portal.thirdweb.com/) vous donnera plus d'informations si vous en avez besoin.

### Utiliser la page TokenERC721

1. Allez sur la page [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721). Elle s'appuie sur leur contrat pour créer une collection NFT.
2. Cliquez sur "Deploy Now" pour accéder à la page de déploiement de contrat.

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

3. Remplissez la section "Contract metadata" :
   1. Téléversez l'image que vous souhaitez représenter votre collection NFT (ou simplement l'image que vous souhaitez minter).
   2. Donnez-lui un nom unique et un symbole/ticker à votre collection.
   3. Ajoutez une description.
4. thirdweb remplit automatiquement les adresses destinataires pour "Primary Sales" et "Royalties", assurez-vous de vérifier que les adresses Web3 sont bien celles de votre portefeuille.
5. Dans "Deploy Options", choisissez Chiliz Chain (ou Chiliz Spicy Testnet s'il s'agit d'un test) et cliquez sur "Deploy Now" :

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

6. thirdweb commencera à déployer le contrat, déclenchant une modale de confirmation depuis votre portefeuille Web3. Confirmez-la pour terminer le déploiement.\
   ![](/files/pYcrEc9o4YwjqLOeAgL2)
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 checklist/barre de progression :\
   ![](/files/aK3XtuEU3nSg2O5ObRnF)
8. Comme vous pouvez le voir depuis la checklist, pour l'instant tout ce que vous avez fait est déployer un contrat, vous devez encore minter réellement 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/TKkMxT20Xs1tY0Bq2hOh)
9. Cliquez sur le bouton "Mint" et un panneau latéral s'ouvre avec des champs pour chaque attribut de votre NFT. C'est ici que vous créez les métadonnées pour votre NFT :\
   ![](/files/4EI3K1Vupt0r2Ak98dld)
10. Remplissez les différents champs. Vous pouvez créer des champs spécifiques à votre collection 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 et approuvez la transaction depuis votre portefeuille.\
    ![](/files/FhQS1NqBtYz7LiCgf0im)
12. thirdweb affichera ensuite la page "NFTs", avec votre NFT maintenant visible, attaché à votre contrat NFT.

TERMINÉ ! Vous avez minté votre NFT !

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

<figure><img src="/files/lBW6MA2oY5RLXwNuwf1m" 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 marketplaces comme [Rarible](https://rarible.com/) ou [OKX](https://www.okx.com/), et à partir de là, vous pouvez commencer à le vendre !

### Utiliser le bouton "Create Token"

Il s'agit en fait d'une méthode plus directe que celle ci-dessus, et c'est une addition récente à 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/cxffwLnp7AaPBWU9Pbs7)
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/VXdTLkDTgEtoEFh3Q9k6)
3. Vous serez redirigé vers une version étape par étape de la page de déploiement [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) (en pratique, elle 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 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/juN1etptLzfOCwa5enUX)
7. Une fois cela fait, cliquez sur le bouton "View NFT", et vous serez redirigé vers votre tableau de bord thirdweb, affichant la page spécifique à ce contrat — et ses NFTs attachés.

À partir de là, vous pouvez vérifier qu'il est bien sur la chaîne via un explorateur de blocs, ou commencer à vendre votre NFT via un marketplace.

## Minter un seul NFT de manière programmatique

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

Cela nécessitera que vous disposiez de :

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

```bash
npm install thirdweb dotenv
```

Le paramètre `dotenv` génère un fichier `.env`, nécessaire pour stocker les 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=My Chiliz NFT
DESCRIPTION=Minted on Chiliz Chain
```

{% hint style="danger" %}
N'exposez jamais votre `PRIVATE_KEY` ni `THIRDWEB_SECRET_KEY` dans du code côté client !\
Gardez ce fichier sur un serveur sécurisé/CI, et ne le téléversez pas sur 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 sur IPFS
2. Construire et téléverser le fichier `metadata.json`
3. Minter le NFT en utilisant la méthode mintTo du contrat.
4. Vous afficher le `tokenId` et le `tokenUrl` résultants.

{% hint style="success" %}
Il s'agit uniquement d'un exemple de code, vous devrez l'adapter à votre propre code de 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 sur 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] }); // retourne ipfs://... 

  // Construire et téléverser metadata.json référençant l'imageUri 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] });

  // Préparer et envoyer la transaction mintTo en utilisant l'URI de métadonnées
  const tx = mintTo({
    contract,
    to: account.address,
    nft: metadataUri, // Le tokenURI
  });

  const { transactionHash } = await sendTransaction({ transaction: tx, account });

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

  // Parser l'événement Transfer pour afficher le 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;

  if (tokenId !== undefined) {
    const nft = await getNFT({ contract, tokenId });
  }
}

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

{% endcode %}

Les parties importantes dont s'inspirer sont :

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

## Minter une collection de NFTs de manière programmatique

Minter une collection de NFTs n'est pas très différent d'en minter un seul. La différence la plus notable est que votre fichier `.env` pointe vers le dossier contenant 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édias.
NAME_PREFIX=My Chiliz NFT              # Deviendra : "My Chiliz NFT <n>"
DESCRIPTION=Minted on Chiliz Chain
```

L'exemple de code représenté ici en tient donc compte :

{% 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édias dans un dossier sur IPFS
  const dirPath = process.env.IMAGES_DIR!;
  const filePaths = listMediaFiles(dirPath);
  if (!filePaths.length) {
    throw new Error(`No media files found in ${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 }); // retourne ["ipfs://...", ...]

  // Construire et téléverser chaque metadata.json référençant son imageUri
  const namePrefix = process.env.NAME!;
  const description = process.env.DESCRIPTION!;

  // Créer un tableau d'objets File pour les JSONs 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 });

  // Préparer et envoyer les transactions mintTo en utilisant les URIs de métadonnées (séquentiel)
  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 });

    // Parser l'événement Transfer pour obtenir le tokenId pour 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;

    if (tokenId !== undefined) {
      const nft = await getNFT({ contract, tokenId });
    }
  }
}

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

```

{% endcode %}

Les changements détaillés sont :

* `IMAGES_DIR` remplace `IMAGE_PATH`.
* Ajout de `listMediaFiles` pour rassembler plusieurs fichiers d'entrée.
* `upload` par lots pour les images et pour les métadonnées (conserve l'ordre).
* Boucle pour `mintTo` chaque `metadataUri`, parsant chaque événement `Transfer` pour son `tokenId`.
* Les noms sont auto-numérotés : `NAME #1`, `NAME #2`, … en utilisant votre `NAME`/`DESCRIPTION` existants.

Encore une fois, utilisez 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 dispose d'une [méthode `lazyMint`](https://portal.thirdweb.com/references/typescript/v5/erc721/lazyMint).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.chiliz.com/fr/developper/notions-avancees/travailler-avec-les-nfts/minter-avec-thirdweb.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
