> 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/vi/phat-trien/nang-cao/lam-viec-voi-nft/duc-voi-rarible.md).

# Đúc NFT với Rarible

Rarible là một giao thức và sàn giao dịch NFT đa chuỗi, cung cấp SDK và API để xây dựng ứng dụng NFT trên nhiều mạng EVM.

Để đúc NFT, [Rarible Multichain SDK](https://docs.rarible.org/reference/getting-started) cho phép bạn đúc vào các bộ sưu tập của riêng mình hoặc bộ sưu tập chung, đồng thời tạo lệnh bán ngay lập tức.

Vì Rarible SDK không xử lý việc tải lên IPFS, các ví dụ ở đây sử dụng [Pinata](https://pinata.cloud/) làm IPFS host. Bạn cần có tài khoản Pinata để lấy JWT key.

Ngoài ra, bạn cũng cần:

* Một hợp đồng ERC-721 đã được triển khai trên Chiliz Chain. Bạn cần tự triển khai [hợp đồng OpenZeppelin ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) trên Chiliz Chain. [Remix IDE](https://remix.ethereum.org/) cung cấp cho bạn môi trường lập trình ngay trên trình duyệt để thực hiện điều đó. Đừng quên xác minh hợp đồng bằng block explorer!
* Một Rarible API key. Xem hướng dẫn Getting Started của họ để biết cách lấy:

{% embed url="<https://docs.rarible.org/docs/rarible-protocol>" %}

Để sử dụng Rarible SDK, chúng ta cần cài đặt một số gói:

```bash
npm i @rarible/sdk @rarible/types ethers dotenv pinata
```

{% hint style="warning" %}
Bạn sẽ nhận thấy rằng chúng ta đang sử dụng `ethers`, đây là một lựa chọn thay thế cho `viem` mà chúng ta đã dùng ở các phần khác.\
Thực ra, [Rarible khuyến nghị](https://docs.rarible.org/reference/getting-started#interacting-with-evm-wallets) các nhà phát triển nên sử dụng ethers.js hoặc web3.js để làm việc với ví Web3 — và [web3.js](https://web3js.org/#/) [đã ngừng hoạt động vào tháng 3 năm 2025](https://blog.chainsafe.io/web3-js-sunset/), chỉ còn lại `ethers` là lựa chọn thực tế duy nhất.
{% endhint %}

## Đúc một bộ sưu tập NFT

Đầu tiên, hãy tạo file .env của bạn:

```
# Chiliz RPC
RPC_URL=https://spicy-rpc.chiliz.com       # hoặc một Mainnet RPC
PRIVATE_KEY=0xabc...                       # khóa của người đúc (CHỈ dùng phía server)

# Rarible
RARIBLE_API_KEY=your_rarible_api_key
COLLECTION_ADDRESS=0xYourCollectionOnChiliz
SUPPLY=1                                   # 1 = ERC-721, >1 = ERC-1155

# Pinata
PINATA_JWT=eyJhbGciOi...                   # Pinata JWT từ dashboard
PINATA_GATEWAY=your-subdomain.mypinata.cloud

# Đầu vào media / metadata
IMAGE_PATH=./art/image.png
NAME=My Chiliz NFT
DESCRIPTION=Minted on-chain via Rarible SDK

```

Quy trình làm việc của chúng ta là:

1. Tải media lên IPFS qua Pinata
2. Xây dựng & tải lên file metadata
3. Rarible SDK + đúc on-chain

Dưới đây là script mẫu đầy đủ để bạn tham khảo:

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

```bash
import 'dotenv/config'
import fs from 'fs'
import path from 'path'
import { Wallet, JsonRpcProvider } from 'ethers'
import { createRaribleSdk } from '@rarible/sdk'
import { toCollectionId, toUnionAddress } from '@rarible/types'
import { PinataSDK } from 'pinata'

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() {
  // Upload media to IPFS
  const pinata = new PinataSDK({
    pinataJwt: process.env.PINATA_JWT!,
  })

  const mediaPath = process.env.IMAGE_PATH!
  const mediaBlob = new Blob([fs.readFileSync(mediaPath)], { type: guessMime(mediaPath) })
  const mediaFile = new File([mediaBlob], path.basename(mediaPath), { type: guessMime(mediaPath) })

  const mediaUpload = await pinata.upload.public.file(mediaFile)
  const imageUri = `ipfs://${mediaUpload.cid}`
  // console.log('imageUri:', imageUri)

  // Build & upload metadata file
  const metadata = {
    name: process.env.NAME!,
    description: process.env.DESCRIPTION!,
    image: imageUri,
    // animation_url: "ipfs://<cid>/video.mp4",
    // attributes: [{ trait_type: "Tier", value: "Gold" }],
  }
  const metaUpload = await pinata.upload.public.json(metadata)
  const metadataUri = `ipfs://${metaUpload.cid}`
  // console.log('metadataUri:', metadataUri)

  // Rarible on-chain mint
  const provider = new JsonRpcProvider(process.env.RPC_URL!)
  const wallet = new Wallet(process.env.PRIVATE_KEY!, provider)
  const sdk = createRaribleSdk(wallet, 'prod', { apiKey: process.env.RARIBLE_API_KEY! })

  // Union IDs use "<BLOCKCHAIN>:<address>" — here BLOCKCHAIN is "CHILIZ"
  const collectionId = toCollectionId(`CHILIZ:${process.env.COLLECTION_ADDRESS}`)
  const creator = toUnionAddress(`CHILIZ:${wallet.address}`)

  const { transaction, itemId } = await sdk.nft.mint({
    collectionId,
    uri: metadataUri,
    supply: Number(process.env.SUPPLY || 1), // 1 for 721, >1 for 1155
  })

  const { hash } = await transaction.wait()
  console.log('Minted item:', itemId, 'tx:', hash)
}

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

```

{% endcode %}

## Lazy-minting một bộ sưu tập NFT

Lazy Minting là tùy chọn dành cho những ai không muốn trả chi phí đúc NFT trước khi đưa lên bán. Bạn có thể niêm yết NFT để bán, sau đó chỉ đúc khi có người mua hoặc chuyển nhượng. Phí gas vì vậy là một phần của quá trình đúc và do người mua chi trả.

Nói ngắn gọn, lazy-minting nghĩa là đưa NFT lên blockchain chỉ khi có người mua NFT đó, chứ không phải trước đó.

Bạn cần có file .env được định nghĩa đúng cách:

```
# Chiliz RPC / wallet
RPC_URL=https://spicy-rpc.chiliz.com
PRIVATE_KEY=0xabc...                         # khóa của người tạo (giữ bí mật)

# Rarible
RARIBLE_API_KEY=your_rarible_api_key
COLLECTION_ADDRESS=0xYourCollectionOnChiliz   # ERC-721 hoặc ERC-1155

# Pinata
PINATA_JWT=eyJhbGciOi...                      # Pinata JWT từ dashboard
PINATA_GATEWAY=your-subdomain.mypinata.cloud  # tùy chọn (để xem trước)

# Media / metadata
IMAGE_PATH=./art/image.png
NAME=My Chiliz NFT
DESCRIPTION=Lazy-minted on Rarible (Chiliz)
SUPPLY=1                                      # 1 cho 721; >1 cho các phiên bản 1155

```

Và đây là code mẫu để lazy-mint dự án của bạn:

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

```typescript
import 'dotenv/config'
import fs from 'fs'
import path from 'path'
import { Wallet, JsonRpcProvider } from 'ethers'
import { createRaribleSdk } from '@rarible/sdk'
import { toCollectionId, toUnionAddress } from '@rarible/types'
import { PinataSDK } from 'pinata'

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() {
  // Upload media to IPFS
  const pinata = new PinataSDK({
    pinataJwt: process.env.PINATA_JWT!,
  })

  const mediaPath = process.env.IMAGE_PATH!
  const mediaBlob = new Blob([fs.readFileSync(mediaPath)], { type: guessMime(mediaPath) })
  const mediaFile = new File([mediaBlob], path.basename(mediaPath), { type: guessMime(mediaPath) })
  const mediaUp = await pinata.upload.public.file(mediaFile)
  const imageUri = `ipfs://${mediaUp.cid}`

  const metadata = {
    name: process.env.NAME!,
    description: process.env.DESCRIPTION!,
    image: imageUri,
    // animation_url: "ipfs://<cid>/video.mp4",
    // attributes: [{ trait_type: "Tier", value: "Gold" }],
  }
  const metaUp = await pinata.upload.public.json(metadata)
  const metadataUri = `ipfs://${metaUp.cid}`

  // Rarible lazy mint
  const provider = new JsonRpcProvider(process.env.RPC_URL!)
  const wallet = new Wallet(process.env.PRIVATE_KEY!, provider)
  const sdk = createRaribleSdk(wallet, 'prod', { apiKey: process.env.RARIBLE_API_KEY! })
  const collectionId = toCollectionId(`CHILIZ:${process.env.COLLECTION_ADDRESS}`)
  const creator = toUnionAddress(`CHILIZ:${wallet.address}`)

  // Prepare & submit lazy mint (off-chain item; buyer mints on purchase)
  const prepared = await sdk.nft.mint.prepare({ collectionId })
  const result = await prepared.submit({
    uri: metadataUri,
    supply: Number(process.env.SUPPLY || 1),     // 1 for 721; >1 for 1155
    lazyMint: true,                              // Yes, make it lazy please
    creators: [{ account: creator, value: 10000 }], // 100% go to your wallet
    royalties: [],                               // e.g. [{ account: creator, value: 500 }] for 5%
  })

  // console.log('Lazy itemId:', result.itemId)
}

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

```

{% endcode %}


---

# 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/vi/phat-trien/nang-cao/lam-viec-voi-nft/duc-voi-rarible.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.
