> 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/th/phatthana/sung-khuen/nft/mint-thirdweb.md).

# Mint NFT ด้วย thirdweb

[thirdweb](https://thirdweb.com/) คือแพลตฟอร์มที่มีเครื่องมือและโครงสร้างพื้นฐานสำหรับสร้างแอปพลิเคชันแบบกระจายศูนย์บนหลาย blockchain network โดยทำให้การผสานรวม blockchain ง่ายขึ้นด้วย SDK, API และ component สำเร็จรูป

สำหรับการสร้าง NFT นั้น thirdweb มี dashboard ที่สวยงาม การอัปโหลด IPFS พร้อมกับสัญญาที่ผ่านการตรวจสอบแล้ว และ SDK ที่ครอบคลุม

## การสร้าง NFT เดี่ยวแบบ Manual

หากคุณต้องการอัปโหลดรูปภาพเดียวและสร้าง NFT thirdweb มีอินเทอร์เฟซแบบคลิกเพื่อปรับใช้

ขั้นแรก สร้างบัญชี thirdweb โดยใช้ Web3 wallet ของคุณเป็นเจ้าของ ซึ่งจะให้คุณเข้าถึง dashboard ของ thirdweb ได้

จากนั้น คุณสามารถเลือกได้สองเส้นทาง:

* ใช้หน้า [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) และปุ่ม "Deploy Now"
* หรือใช้อินเทอร์เฟซ "Create Token" ใน dashboard

มาดูแต่ละวิธีกันอย่างรวดเร็วในหน้านี้\
[thirdweb Developer Portal](https://portal.thirdweb.com/) จะให้ข้อมูลเพิ่มเติมหากคุณต้องการ

### การใช้หน้า TokenERC721

1. ไปที่หน้า [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) ซึ่งใช้สัญญาของ thirdweb สำหรับสร้างคอลเลกชัน NFT
2. คลิก "Deploy Now" เพื่อเข้าสู่หน้าปรับใช้สัญญา

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

3. กรอกข้อมูลในส่วน "Contract metadata":
   1. อัปโหลดรูปภาพที่คุณต้องการแทนคอลเลกชัน NFT ของคุณ (หรือรูปภาพที่คุณต้องการสร้าง)
   2. ตั้งชื่อและสัญลักษณ์/ticker ที่ไม่ซ้ำกันให้กับคอลเลกชันของคุณ
   3. ใส่คำอธิบาย
4. thirdweb จะกรอกที่อยู่ผู้รับสำหรับ "Primary Sales" และ "Royalties" โดยอัตโนมัติ ตรวจสอบให้แน่ใจว่าที่อยู่ Web3 เหล่านั้นเป็นของ กระเป๋าเงิน ของคุณจริงๆ
5. ใน "Deploy Options" เลือก Chiliz Chain (หรือ Chiliz Spicy Testnet หากนี่เป็นเพียงการทดสอบ) และคลิก "Deploy Now":

<figure><img src="/files/3znoTbzKjSCnNgM2aaT5" alt=""><figcaption></figcaption></figure>

6. thirdweb จะเริ่มปรับใช้สัญญา โดยเปิด modal ยืนยันจาก Web3 wallet ของคุณ ยืนยันเพื่อเสร็จสิ้นการปรับใช้\
   ![](/files/5ry4og9TUpIFwsa1KzuQ)
7. เมื่อเสร็จแล้ว คลิกปุ่ม "View Contract" คุณจะถูกพาไปยังหน้าของสัญญาใน dashboard ของ thirdweb พร้อม checklist/progress bar:\
   ![](/files/Za5TglxMmHg6TpjkVRhO)
8. ดังที่เห็นจาก checklist สิ่งที่คุณทำไปตอนนี้คือการปรับใช้สัญญาเท่านั้น คุณยังต้องสร้าง NFT จริงๆ ในคอลัมน์ด้านซ้าย คลิกเมนู "NFTs" เพื่อเปิดหน้าใหม่ซึ่งตอนนี้ว่างเปล่า:\
   ![](/files/uueOR82r6DlIWiOVmah3)
9. คลิกปุ่ม "Mint" และ side-panel จะเปิดขึ้นพร้อมช่องสำหรับแต่ละ attribute ของ NFT คุณ นี่คือที่ที่คุณสร้าง metadata สำหรับ NFT ของคุณ:\
   ![](/files/iwINqKY6BZ4jyuFFrQvk)
10. กรอกข้อมูลในช่องต่างๆ คุณสามารถสร้างช่องที่เฉพาะเจาะจงสำหรับคอลเลกชัน NFT ของคุณ เช่น "Artist\_name" หรือ "Type\_of\_work" และสำรวจตัวเลือกขั้นสูง แต่สำหรับการทดสอบนี้ คุณสามารถทำให้มันเรียบง่าย: ชื่อ ไฟล์มีเดีย คำอธิบาย
11. คลิกปุ่ม "Mint NFT" ที่ด้านล่างของ side-panel และอนุมัติ ธุรกรรม จาก กระเป๋าเงิน ของคุณ\
    ![](/files/E6Tm9Sp6jj9irvcGAuFX)
12. thirdweb จะแสดงหน้า "NFTs" พร้อม NFT ของคุณที่มองเห็นได้แล้ว ซึ่งแนบกับสัญญา NFT ของคุณ

เสร็จแล้ว! คุณได้สร้าง NFT เรียบร้อยแล้ว!

เพื่อตรวจสอบว่ามันอยู่บน Chiliz Chain จริงๆ เปิด [Chiliscan](https://www.chiliscan.com/) (หรือ [Testnet variant](https://testnet.chiliscan.com/) หากคุณใช้ Spicy Testnet) และวาง hash ของ NFT ในช่องค้นหา ซึ่งจะยืนยันทันทีว่าสัญญาของคุณอยู่บน chain และแท็บ "Inventory" จะแสดง NFT ของคุณพร้อมคุณ (หรืออย่างน้อย ID ของ กระเป๋าเงิน ของคุณ) เป็นเจ้าของ

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

หากคุณสร้าง NFT บน Chiliz Chain Mainnet คุณควรเห็นมันปรากฏในบัญชีของคุณบน marketplace เช่น [Rarible](https://rarible.com/) หรือ [OKX](https://www.okx.com/) และจากนั้น คุณสามารถเริ่มขายได้!

### การใช้ปุ่ม "Create Token"

นี่เป็นวิธีที่ตรงไปตรงมามากกว่าวิธีด้านบน และเป็นฟีเจอร์ที่เพิ่งเพิ่มเข้ามาใน thirdweb

1. จากโฟลเดอร์โปรเจกต์ใดๆ ใน dashboard ของ thirdweb คลิกตัวเลือก "Tokens" ในแถบด้านซ้าย:\
   ![](/files/lf6ZCF0vazQLCXYDUy1P)
2. ในหน้า "Tokens" ที่เปิดขึ้น คลิกปุ่ม "Create Token" ทางด้านขวา จะมีหน้าต่าง modal พร้อม 2 ตัวเลือก เลือก "Create NFT Collection"\
   ![](/files/XDyVx8zpSUafPzk46q5A)
3. ซึ่งจะพาคุณไปยังเวอร์ชันแบบ step-by-step ของหน้าปรับใช้ [TokenERC721](https://thirdweb.com/thirdweb.eth/TokenERC721) (ในความเป็นจริง มันจะปรับใช้ ERC-721 Drop contract) กรอกข้อมูลในช่องทั้งหมดที่จำเป็นสำหรับสัญญานี้ จากนั้นกดปุ่ม "Next"
4. หน้าจอถัดไปเกี่ยวกับตัว NFT เอง: อัปโหลดรูปภาพและกรอกข้อมูลในช่องที่อธิบายรูปภาพของคุณ จากนั้นกดปุ่ม "Next"
5. thirdweb จะแสดงสรุปคอลเลกชัน NFT ที่คุณกำลังจะเปิดตัว ตรวจสอบว่าทุกอย่างถูกต้องแล้วคลิกปุ่ม "Launch NFT Collection"
6. thirdweb จะรับผิดชอบการปรับใช้สัญญา การสร้าง NFT และการตั้งเงื่อนไขทั้งหมดในครั้งเดียว คุณจะต้องยืนยันสาม ธุรกรรม ผ่าน Web3 wallet ของคุณ\
   ![](/files/btQXTVbUpgOg0j0dgYqr)
7. เมื่อเสร็จแล้ว คลิกปุ่ม "View NFT" และคุณจะถูกพากลับไปยัง dashboard ของ thirdweb ซึ่งแสดงหน้าที่เฉพาะเจาะจงสำหรับสัญญานี้ พร้อม NFT ที่แนบอยู่

จากนั้น คุณสามารถตรวจสอบว่ามันอยู่บน chain จริงๆ ผ่าน block explorer หรือเริ่มขาย NFT ของคุณผ่าน marketplace

## การสร้าง NFT เดี่ยวแบบโปรแกรม

นี่คือตัวอย่างโค้ดที่ใช้ [thirdweb v5 SDK](https://portal.thirdweb.com/references/typescript/v5) ซึ่งอัปโหลดไฟล์มีเดียไปยัง IPFS สร้างไฟล์ `metadata.json` จากนั้นอัปโหลดไปยัง IPFS ด้วย

สิ่งที่คุณต้องมี:

* สัญญา ERC-721 ที่ปรับใช้แล้วบน Chiliz Chain ในกรณีนี้คุณควรทำผ่าน thirdweb แทนที่จะใช้เครื่องมืออื่น
* API key ของ thirdweb ที่เชื่อมกับสัญญา ERC-721 ที่คุณปรับใช้ผ่าน thirdweb คุณสามารถหาได้ในหน้าโปรเจกต์ของ smart contract ภายใต้ชื่อ "Client ID"
* การติดตั้ง thirdweb SDK ในเครื่อง คุณสามารถทำได้โดยใช้ `npm`:

```bash
npm install thirdweb dotenv
```

พารามิเตอร์ `dotenv` สร้างไฟล์ `.env` ซึ่งจำเป็นสำหรับเก็บค่าส่วนตัว:

```
THIRDWEB_SECRET_KEY=YOUR_TW_SECRET_KEY # จากหน้าโปรเจกต์ thirdweb ของคุณ
PRIVATE_KEY=0xabc...                   # Web3 wallet ที่จะส่ง transaction
CONTRACT_ADDRESS=0xYour721Address      # สัญญา TokenERC721 ที่ปรับใช้แล้วของคุณ
CHAIN_ID=88882                         # 88888=Mainnet, 88882=Spicy
IMAGE_PATH=./art/image.png             # เส้นทาง local ของไฟล์มีเดียของคุณ
NAME=My Chiliz NFT
DESCRIPTION=Minted on Chiliz Chain
```

{% hint style="danger" %}
อย่าเปิดเผย `PRIVATE_KEY` หรือ `THIRDWEB_SECRET_KEY` ในโค้ดฝั่ง client เด็ดขาด!\
เก็บไฟล์นี้ไว้บน server/CI ที่ปลอดภัย และอย่าอัปโหลดไปยัง repository สาธารณะ
{% endhint %}

เมื่อทุกอย่างพร้อมแล้ว คุณสามารถนำ script นี้ไปเป็นแนวทาง ซึ่งจะ:

1. อัปโหลดไฟล์มีเดียไปยัง IPFS
2. สร้างและอัปโหลดไฟล์ `metadata.json`
3. สร้าง NFT โดยใช้ method mintTo ของสัญญา
4. แสดง `tokenId` และ `tokenUrl` ที่ได้แก่คุณ

{% hint style="success" %}
นี่เป็นเพียงตัวอย่างโค้ด คุณจะต้องปรับให้เข้ากับโค้ดโปรเจกต์ของคุณเอง!
{% 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 %}

ส่วนสำคัญที่ควรนำไปเป็นแนวทาง:

* `upload` เก็บไฟล์ (และ JSON) ไปยัง IPFS และคืนค่า URI ในรูปแบบ `ipfs://…`
* `mintTo` รับ string เพื่อใช้โดยตรงเป็น `tokenURI`
* เรา parse event `Transfer` มาตรฐาน ERC-721 เพื่ออ่าน `tokenId` ที่ถูกสร้าง

## การสร้างคอลเลกชัน NFT แบบโปรแกรม

การสร้างคอลเลกชัน NFT ไม่ได้แตกต่างจากการสร้าง NFT เดี่ยวมากนัก ความแตกต่างที่สำคัญที่สุดคือไฟล์ `.env` ของคุณชี้ไปยังโฟลเดอร์ที่มีรูปภาพทั้งหมดโดยใช้ `IMAGE_DIR` แทนที่จะชี้ไปยังรูปภาพเดี่ยวด้วย `IMAGE_PATH`

```dotenv
THIRDWEB_SECRET_KEY=YOUR_TW_SECRET_KEY # จากหน้าโปรเจกต์ thirdweb ของคุณ
PRIVATE_KEY=0xabc...                   # Web3 wallet ที่จะส่ง transaction
CONTRACT_ADDRESS=0xYour721Address      # สัญญา TokenERC721 ที่ปรับใช้แล้วของคุณ
CHAIN_ID=88882                         # 88888=Mainnet, 88882=Spicy
IMAGE_PATH=./art                       # เส้นทาง local ของไฟล์มีเดียของคุณ
NAME_PREFIX=My Chiliz NFT              # จะกลายเป็น: "My Chiliz NFT <n>"
DESCRIPTION=Minted on Chiliz Chain
```

ตัวอย่างโค้ดที่เราแสดงที่นี่จึงคำนึงถึงสิ่งนี้:

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

การเปลี่ยนแปลงที่ละเอียดกว่านั้นคือ:

* `IMAGES_DIR` แทนที่ `IMAGE_PATH`
* เพิ่ม `listMediaFiles` เพื่อรวบรวม input หลายไฟล์
* `upload` แบบ batch สำหรับรูปภาพและ metadata (รักษาลำดับ)
* Loop เพื่อ `mintTo` แต่ละ `metadataUri` โดย parse event `Transfer` แต่ละรายการเพื่อดึง `tokenId`
* ชื่อจะถูกกำหนดหมายเลขอัตโนมัติ: `NAME #1`, `NAME #2`, … โดยใช้ `NAME`/`DESCRIPTION` ของคุณ

อีกครั้ง โปรดใช้สิ่งนี้เป็นแรงบันดาลใจสำหรับโค้ดของคุณเอง อย่าใช้โดยตรง!

หมายเหตุ: หากคุณต้องการทำ lazy minting สัญญา ERC-721 ของ thirdweb มี [method `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/th/phatthana/sung-khuen/nft/mint-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.
