> 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/pl/tworzenie/zaawansowane/praca-z-nft/mintowanie-z-thirdweb.md).

# Mintowanie z thirdweb

[thirdweb](https://thirdweb.com/) to platforma oferująca narzędzia i infrastrukturę do budowania zdecentralizowanych aplikacji w wielu sieciach blockchain. Upraszcza integrację z blockchainem dzięki SDK, API i gotowym komponentom.

W przypadku mintowania NFT, thirdweb oferuje dopracowany dashboard i upload na IPFS, a także swoje audytowane kontrakty i rozbudowane SDK.

## Ręczne mintowanie pojedynczego NFT

Jeśli chcesz po prostu przesłać pojedynczy obraz i go zmintować, thirdweb oferuje interfejs typu kliknij-i-wdróż.

Najpierw utwórz konto thirdweb, używając swojego portfela Web3 jako właściciela. Daje to dostęp do twojego dashboardu thirdweb.

Stamtąd możesz wybrać jedną z dwóch ścieżek:

* Użyj ich strony [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) i jej przycisku "Deploy Now".
* Albo użyj interfejsu "Create Token" w dashboardzie.

Przyjrzyjmy się szybko każdej z nich na tej stronie.\
[Portal dla deweloperów thirdweb](https://portal.thirdweb.com/) dostarczy ci więcej informacji, jeśli będziesz ich potrzebować.

### Korzystanie ze strony TokenERC721

1. Przejdź na stronę [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721). Opiera się ona na ich kontrakcie do tworzenia kolekcji NFT.
2. Kliknij "Deploy Now", aby uzyskać dostęp do strony wdrażania kontraktu.

<figure><img src="/files/45ZL0MIPJC0djobj1ST1" alt=""><figcaption></figcaption></figure>

3. Wypełnij sekcję "Contract metadata":
   1. Prześlij obraz, który ma reprezentować twoją kolekcję NFT (albo po prostu obraz, który chcesz zmintować).
   2. Nadaj swojej kolekcji unikalną nazwę oraz symbol/ticker.
   3. Nadaj jej opis.
4. thirdweb automatycznie wypełnia adresy odbiorców dla "Primary Sales" i "Royalties" — upewnij się, że adresy Web3 to faktycznie adresy twojego portfela.
5. W sekcji "Deploy Options" wybierz Chiliz Chain (lub Chiliz Spicy Testnet, jeśli to tylko test) i kliknij "Deploy Now":

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

6. thirdweb rozpocznie wdrażanie kontraktu, wywołując okno potwierdzenia z twojego portfela Web3. Potwierdź je, aby dokończyć wdrażanie.\
   ![](/files/SNEFO83n4TJI2MEVheME)
7. Gdy to zrobisz, kliknij przycisk "View Contract". Zostaniesz przeniesiony na stronę kontraktu w twoim dashboardzie thirdweb, z listą kontrolną/paskiem postępu:\
   ![](/files/mMJIaTkRGg0BURdKc1US)
8. Jak widać z listy kontrolnej, na razie wszystko, co zrobiłeś, to wdrożenie kontraktu — wciąż musisz faktycznie zmintować NFT. W lewej kolumnie kliknij pozycję menu "NFTs", aby otworzyć nową stronę, która jest na razie pusta:\
   ![](/files/O3k3ctw14bCIhzHtTvN4)
9. Kliknij przycisk "Mint", a otworzy się panel boczny z polem dla każdego atrybutu twojego NFT. To tutaj tworzysz metadane dla swojego NFT:\
   ![](/files/QZevXTjlumvSwhKbgV0Z)
10. Wypełnij poszczególne pola. Możesz tworzyć pola specyficzne dla twojej kolekcji NFT, takie jak "Artist\_name" czy "Type\_of\_work", oraz eksplorować opcje zaawansowane, ale na potrzeby tego testu możesz ograniczyć się do minimum: nazwa, plik multimedialny, opis.
11. Kliknij przycisk "Mint NFT" na dole panelu bocznego i zatwierdź transakcję ze swojego portfela.\
    ![](/files/bOii3anzo9HtgmHvRhoN)
12. thirdweb wyświetli następnie stronę "NFTs", na której twoje NFT będzie teraz widoczne, dołączone do twojego kontraktu NFT.

GOTOWE! Zmintowałeś swoje NFT!

Aby sprawdzić, czy faktycznie znajduje się na Chiliz Chain, otwórz [Chiliscan](https://www.chiliscan.com/) (lub [jego wariant Testnet](https://testnet.chiliscan.com/), jeśli używasz Spicy Testnet) i skopiuj-wklej hash NFT w pole wyszukiwania. Powinno to od razu potwierdzić, że twój kontrakt jest na chainie, a jego zakładka "Inventory" powinna wymieniać twoje NFT z tobą (lub przynajmniej z ID twojego portfela) jako właścicielem.

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

Jeśli zmintowałeś swoje NFT na Chiliz Chain Mainnet, powinieneś zobaczyć je na swoim koncie na marketplace'ach takich jak [Rarible](https://rarible.com/) czy [OKX](https://www.okx.com/), i stamtąd możesz zacząć je sprzedawać!

### Korzystanie z przycisku "Create Token"

Jest to w istocie prostszy sposób niż ten powyżej i stanowi niedawny dodatek do thirdweb.

1. Z dowolnego folderu projektu w twoim dashboardzie thirdweb kliknij opcję "Tokens" w lewym pasku bocznym:\
   ![](/files/Pn26Z3aJ32TVWZk9DAc6)
2. Na stronie "Tokens", która się otworzy, kliknij przycisk "Create Token" po prawej stronie. Otworzy się okno modalne z 2 opcjami — wybierz "Create NFT Collection".\
   ![](/files/deU7K1hZXJCnBjKzdWjp)
3. Przeniesie cię to do wersji krok-po-kroku strony wdrażania [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) (w efekcie wdroży kontrakt ERC-721 Drop). Wypełnij wszystkie pola niezbędne dla tego kontraktu, a następnie naciśnij przycisk "Next".
4. Następny ekran dotyczy samego NFT: prześlij obraz i wypełnij pola opisujące twój obraz, a następnie naciśnij przycisk "Next".
5. thirdweb wyświetli teraz podsumowanie kolekcji NFT, którą zamierzasz uruchomić. Sprawdź, czy wszystko się zgadza, a następnie kliknij przycisk "Launch NFT Collection".
6. thirdweb zajmie się wdrożeniem kontraktu, zmintowaniem NFT i ustawieniem warunków — wszystko za jednym razem. Będziesz musiał potwierdzić trzy transakcje przez swój portfel Web3.\
   ![](/files/AkyxxO1zy4Qx1snL1VJM)
7. Gdy to się zakończy, kliknij przycisk "View NFT", a zostaniesz przeniesiony z powrotem do twojego dashboardu thirdweb, wyświetlając stronę specyficzną dla tego kontraktu — i dołączonych do niego NFT.

Stamtąd możesz sprawdzić, czy faktycznie znajduje się na chainie, za pomocą eksploratora bloków, albo zacząć sprzedawać swoje NFT przez marketplace.

## Programistyczne mintowanie pojedynczego NFT

Oto przykładowy kod wykorzystujący [thirdweb v5 SDK](https://portal.thirdweb.com/references/typescript/v5), który przesyła plik multimedialny na IPFS, generuje plik `metadata.json`, a następnie również przesyła go na IPFS.

Będzie to wymagać, abyś posiadał:

* Już wdrożony kontrakt ERC-721 na Chiliz Chain. W tym przypadku powinieneś zrobić to za pomocą thirdweb, a nie innego narzędzia.
* Klucz API thirdweb, powiązany z kontraktem ERC-721, który wdrożyłeś przez thirdweb. Znajdziesz go na stronie projektu smart contractu, pod nazwą "Client ID".
* Lokalną instalację thirdweb SDK. Możesz to zrobić za pomocą `npm`:

```bash
npm install thirdweb dotenv
```

Parametr `dotenv` generuje plik `.env`, który jest niezbędny do przechowywania prywatnych wartości:

```
THIRDWEB_SECRET_KEY=YOUR_TW_SECRET_KEY # From your thirdweb project's page.
PRIVATE_KEY=0xabc...                   # The Web3 wallet that will send the transaction.
CONTRACT_ADDRESS=0xYour721Address      # Your deployed TokenERC721 contract.
CHAIN_ID=88882                         # 88888=Mainnet, 88882=Spicy
IMAGE_PATH=./art/image.png             # The local path to your media file.
NAME=My Chiliz NFT
DESCRIPTION=Minted on Chiliz Chain
```

{% hint style="danger" %}
Nigdy nie ujawniaj swojego `PRIVATE_KEY` ani `THIRDWEB_SECRET_KEY` w kodzie po stronie klienta!\
Trzymaj ten plik na bezpiecznym serwerze/CI i nie przesyłaj go do publicznego repozytorium.
{% endhint %}

Teraz, gdy wszystko jest gotowe, możesz zainspirować się tym skryptem, który:

1. Prześle plik multimedialny na IPFS
2. Zbuduje i prześle plik `metadata.json`
3. Zmintuje NFT za pomocą metody mintTo kontraktu.
4. Wyświetli ci wynikowe `tokenId` i `tokenUrl`.

{% hint style="success" %}
To tylko przykładowy kod, będziesz musiał dostosować go do kodu własnego projektu!
{% 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 %}

Najważniejsze fragmenty, którymi warto się zainspirować, to:

* `upload` zapisuje pliki (i JSON) na IPFS i zwraca URI w formacie `ipfs://…`.
* `mintTo` przyjmuje string, który jest używany bezpośrednio jako `tokenURI`.
* Parsujemy standardowe zdarzenie ERC-721 `Transfer`, aby odczytać zmintowane `tokenId`.

## Programistyczne mintowanie kolekcji NFT

Mintowanie kolekcji NFT nie różni się aż tak bardzo od mintowania pojedynczego. Najbardziej znacząca różnica polega na tym, że twój plik `.env` wskazuje na folder zawierający wszystkie obrazy za pomocą `IMAGE_DIR`, a nie na pojedynczy obraz za pomocą `IMAGE_PATH`.

```dotenv
THIRDWEB_SECRET_KEY=YOUR_TW_SECRET_KEY # From your thirdweb project's page.
PRIVATE_KEY=0xabc...                   # The Web3 wallet that will send the transaction.
CONTRACT_ADDRESS=0xYour721Address      # Your deployed TokenERC721 contract.
CHAIN_ID=88882                         # 88888=Mainnet, 88882=Spicy
IMAGE_PATH=./art                       # The local path to your media files.
NAME_PREFIX=My Chiliz NFT              # This will become: "My Chiliz NFT <n>"
DESCRIPTION=Minted on Chiliz Chain
```

Przedstawiony tutaj przykładowy kod uwzględnia więc to:

{% 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";
}

// One way to gather all files
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}`,
  });

  // Upload media files in a folder to 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");

  // Build & upload each metadata.json referencing its imageUri
  // Uses NAME and DESCRIPTION envs; names become "<NAME> #<index>"
  const namePrefix = process.env.NAME!;
  const description = process.env.DESCRIPTION!;

  // Create an array of File objects for metadata JSONs
  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);

  // Prepare & send mintTo transactions using the metadata URIs (sequential)
  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 });

    // Parse Transfer event to get tokenId for this 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 %}

Szczegółowe zmiany to:

* `IMAGES_DIR` zastępuje `IMAGE_PATH`.
* Dodano `listMediaFiles` do zebrania wielu plików wejściowych.
* Wsadowy `upload` dla obrazów i dla metadanych (zachowuje kolejność).
* Pętla do `mintTo` każdego `metadataUri`, parsująca każde zdarzenie `Transfer` dla jego `tokenId`.
* Nazwy są automatycznie numerowane: `NAME #1`, `NAME #2`, … przy użyciu twoich istniejących `NAME`/`DESCRIPTION`.

Ponownie, prosimy, użyj tego jako inspiracji dla własnego kodu, nie używaj w niezmienionej postaci!

Uwaga: Jeśli wolisz wykonać lazy minting, kontrakt ERC-721 thirdweb posiada [metodę `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/pl/tworzenie/zaawansowane/praca-z-nft/mintowanie-z-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.
