# 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 de la blockchain avec des SDK, des API et des composants préconstruits.

Dans le cas de la frappe (mint) de NFT, thirdweb propose un tableau de bord soigné et un téléversement sur IPFS ainsi que ses contrats audités et ses SDKs étendus.

## Frapper un seul NFT manuellement

Si vous voulez simplement téléverser une seule image et la frapper, thirdweb propose une interface clic-pour-déployer.

Tout d'abord, créez un compte thirdweb en utilisant votre portefeuille Web3 comme propriétaire. Cela vous donne accès à votre tableau de bord thirdweb.

De là, vous pouvez choisir entre deux voies :

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

Explorons rapidement chacune 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 [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="https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FiOBJ9vEjTgrGDXLvTc1H%2Fimage.png?alt=media&#x26;token=813cbc07-bb5c-4264-bebf-855d6eb6df04" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2F5pwRre3hAs9tnenpIpOo%2Fimage.png?alt=media&#x26;token=97754b66-957f-427e-8ecc-d82d65c0740a" alt=""><figcaption></figcaption></figure>

6. thirdweb commencera à déployer le contrat, déclenchant une fenêtre de confirmation depuis votre portefeuille Web3. Confirmez-la pour terminer le déploiement.\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FZA5AENlXHNJ4t958GMwB%2Fimage.png?alt=media\&token=02d4ed98-a898-4e98-9bff-0d2894d6ed1e)
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 contrôle/barre de progression :\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FJzTrC2bhoCIvPQC5C1bc%2Fimage.png?alt=media\&token=fbddb609-467d-41d5-aa4e-db89756e9fcc)
8. Comme vous pouvez le voir dans la liste de contrôle, pour l'instant tout ce que vous avez fait est de déployer un contrat, vous devez encore effectivement frapper 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 :\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FN8snuMGiNGQ95LpBVYVd%2Fimage.png?alt=media\&token=23637f39-e3a2-4927-8e85-2618b496aa34)
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 pour votre NFT :\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FMHxWQopIf0X3yvrMYQEw%2Fimage.png?alt=media\&token=d6847f12-d9cc-4b0d-820b-703b80727f93)
10. Remplissez les différents champs. Vous pouvez créer des champs spécifiques à votre collection de NFT, tels que "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.&#x20;
11. Cliquez sur le bouton "Mint NFT" en bas du panneau latéral, et approuvez la transaction depuis votre portefeuille. \
    ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FOHQ6CYuKlTU095csZbVm%2Fimage.png?alt=media\&token=c3b9070c-0e0d-4258-a3ab-f4d1e7882405)
12. thirdweb affichera alors la page "NFTs", avec votre NFT désormais visible, attaché à votre contrat NFT.&#x20;

TERMINÉ ! Vous avez frappé 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 l'onglet "Inventory" devrait lister votre NFT avec vous (ou au moins l'ID de votre portefeuille) en tant que propriétaire.

<figure><img src="https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FMIE0awyJCp6V2R7cT5ug%2Fimage.png?alt=media&#x26;token=0a2555c5-1d3b-46dd-9c80-7179d3003598" alt=""><figcaption></figcaption></figure>

Si vous avez frappé votre NFT sur Chiliz Chain Mainnet, vous devriez le voir apparaître sur votre compte sur des places de marché telles que [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 une addition récente à thirdweb.

1. Depuis n'importe quel dossier de projet sur votre tableau de bord thirdweb, cliquez sur l'option "Tokens" dans la barre latérale gauche :\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2Ffhhm3V6WJUMusyKa48hB%2Fimage.png?alt=media\&token=52ee0435-3521-489f-987a-35548593dbf6)
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".\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FPaabgWRx5ip9785p9ohj%2Fimage.png?alt=media\&token=9c2649a6-53b9-4da8-aa69-902062beeba4)
3. Cela vous conduira à une version pas-à-pas de la [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) page de déploiement (en fait, elle déployera 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 se charge de déployer le contrat, de frapper le NFT et de définir les conditions, le tout en une seule opération. Vous devrez confirmer trois transactions via votre portefeuille Web3.\
   ![](https://2383909840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4L7rBH2rmyoBTn93MSdx%2Fuploads%2FcGZUd5TQPujpphUVGXCe%2Fimage.png?alt=media\&token=d634d62d-6022-499f-8ffd-1879effd60aa)
7. Une fois cela fait, cliquez sur le bouton "View NFT", et vous serez ramené à 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 une place de marché.

## Frapper un seul NFT par programmation

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 `metadata.json` fichier, puis le téléverse également sur 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 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 contrat intelligent, sous le nom "Client ID".
* Une installation locale du SDK thirdweb. Vous pouvez le faire en utilisant `npm`:

```bash
npm install thirdweb dotenv
```

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

{% hint style="danger" %}
Ne divulguez jamais votre `PRIVATE_KEY` ni `THIRDWEB_SECRET_KEY` dans du code côté client ! \
Gardez 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 sur IPFS
2. Construire et téléverser le `metadata.json` fichier
3. Frapper le NFT en utilisant la méthode mintTo du contrat.
4. Afficher le `tokenId` et `tokenUrl` pour vous.

{% hint style="success" %}
Ceci n'est qu'un code d'exemple, 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 or 88882
  const account = privateKeyToAccount({ client, privateKey: process.env.PRIVATE_KEY! });

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

  // Upload media file to 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] }); // returns ipfs://... 
  // console.log("imageUri:", imageUri);

  // Build & upload metadata.json referencing IPFS imageUri
  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);

  // Prepare & send mintTo transaction using the metadata URI
  // mintTo accepts a string to use directly as tokenURI.
  const tx = mintTo({
    contract,
    to: account.address,
    nft: metadataUri, // The tokenURI
  });

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

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

  // Parse Transfer event to display 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() ?? "(not found)");

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

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

{% endcode %}

Les parties importantes dont s'inspirer sont :

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

## Frapper une collection de NFTs par programmation

Frapper une collection de NFTs n'est pas très différent de frapper un seul. La différence la plus notable est que votre `.env` file 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édias.
NAME_PREFIX=My Chiliz NFT              # Cela deviendra : "My Chiliz NFT <n>"
DESCRIPTION=Minted on Chiliz Chain
```

Le code d'exemple que nous présentons ici prend donc cela en 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 or 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 d'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 }); // returns ["ipfs://...", ...]
  //console.log("Uploaded images to IPFS:", imageUris.length, "files");

  // Construire et téléverser chaque metadata.json référant son imageUri
  // Utilise les env 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("Uploaded metadata files to IPFS:", metadataUris.length);

  // Préparer et envoyer les transactions mintTo en utilisant les metadata URIs (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, // The 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() ?? "(not found)");
    if (tokenId !== undefined) {
      const nft = await getNFT({ contract, tokenId });
      // console.log(`[${i + 1}/${metadataUris.length}] tokenURI (read back):`, 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.
* Mises en lot `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 pas l'utiliser tel quel !

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