
Lập trình PyCardano Bài 7: Hướng Dẫn Đúc (Mint) và Đốt (Burn) Nhiều NFT Chuẩn CIP-721
Chào mừng các bạn quay trở lại với series Lập trình PyCardano! Ở bài học trước, chúng ta đã thành thạo việc đúc (mint) Fungible Token. Hôm nay, chúng ta sẽ nâng cấp kỹ năng lên một tầm cao mới: Đúc nhiều NFT cùng lúc kèm theo Metadata chuẩn CIP-721 và cách Đốt (Burn) chúng.
Về cơ bản, logic đúc NFT rất giống với Token thông thường, nhưng có hai điểm khác biệt cốt lõi:
-
Số lượng (Quantity): Với Token, bạn có thể đúc hàng triệu đồng. Nhưng với NFT (Non-Fungible Token), số lượng luôn luôn và bắt buộc là 1.
-
Metadata: Mỗi NFT là độc nhất, do đó nó cần một bộ dữ liệu (Metadata) riêng biệt được gắn vào giao dịch theo tiêu chuẩn CIP-721 của hệ sinh thái Cardano.
Hãy cùng bắt tay vào phần đầu tiên: Đúc (Mint) nhiều NFT!
PHẦN 1: MINT MULTIPLE NFTs VỚI METADATA CIP-721
1. Chuẩn bị Môi trường và Kết nối Ví
Trước khi bắt đầu, hãy đảm bảo bạn đã kích hoạt môi trường ảo (venv) và cài đặt đủ các thư viện cần thiết:
pip install pycardano blockfrost-python python-dotenv
Đoạn code đầu tiên sẽ giúp chúng ta nạp các biến môi trường từ file .env, thiết lập mạng lưới (Testnet/Preprod), khôi phục ví từ Mnemonic và kết nối với Blockfrost API để kiểm tra số dư.
import os
import random
import sys
from os.path import exists
from blockfrost import ApiUrls, BlockFrostApi
from dotenv import load_dotenv
from pycardano import *
# 1. Nạp biến môi trường
load_dotenv()
network = os.getenv("BLOCKFROST_NETWORK")
wallet_mnemonic = os.getenv("MNEMONIC")
blockfrost_api_key = os.getenv("BLOCKFROST_PROJECT_ID")
# 2. Map network (testnet → preprod)
if network == "testnet":
base_url = ApiUrls.preprod.value
cardano_network = Network.TESTNET
else:
base_url = ApiUrls.mainnet.value
cardano_network = Network.MAINNET
# 3. Khôi phục ví từ mnemonic
new_wallet = crypto.bip32.HDWallet.from_mnemonic(wallet_mnemonic)
payment_key = new_wallet.derive_from_path(f"m/1852'/1815'/0'/0/0")
staking_key = new_wallet.derive_from_path(f"m/1852'/1815'/0'/2/0")
payment_skey = ExtendedSigningKey.from_hdwallet(payment_key)
staking_skey = ExtendedSigningKey.from_hdwallet(staking_key)
# Địa chỉ ví phát hành và nhận NFT
main_address = Address(
payment_part=payment_skey.to_verification_key().hash(),
staking_part=staking_skey.to_verification_key().hash(),
network=cardano_network,
)
print(f"Địa chỉ ví của bạn: {main_address}")
# 4. Kết nối Blockfrost API & Kiểm tra UTxO
api = BlockFrostApi(project_id=blockfrost_api_key, base_url=base_url)
try:
utxos = api.address_utxos(main_address.encode())
except Exception as e:
if getattr(e, 'status_code', None) == 404:
print("Ví chưa có UTxO nào. Vui lòng Faucet tADA trước khi đúc NFT.")
else:
print(f"Lỗi: {e}")
sys.exit(1)
cardano = BlockFrostChainContext(project_id=blockfrost_api_key, base_url=base_url)
2. Chuẩn bị Dữ Liệu NFT và Policy Key
Chúng ta sẽ tạo data mẫu cho 5 NFT động vật với các chỉ số chiến đấu được tạo ngẫu nhiên. Đồng thời, code sẽ tải (hoặc tạo mới) Policy Key — chiếc chìa khóa định danh quyền sở hữu của bộ sưu tập này.
# Khởi tạo data mẫu cho 5 NFTs
types = ["lion", "elephant", "panda", "sloth", "tiger", "wolf"]
assets = []
for i in range(1, 6):
assets.append({
"name": f"Pycardano_test_NFT_00{i}",
"attack": str(random.randint(1, 70)),
"speed": str(random.randint(1, 70)),
"defense": str(random.randint(1, 70)),
"health": str(random.randint(1, 70)),
"type": random.choice(types),
})
builder = TransactionBuilder(cardano)
# Tạo thư mục lưu khóa chính sách (Policy Keys)
keys_dir = os.path.join(os.path.dirname(__file__), "keys")
os.makedirs(keys_dir, exist_ok=True)
policy_skey_path = os.path.join(keys_dir, "policy.skey")
policy_vkey_path = os.path.join(keys_dir, "policy.vkey")
# Tạo mới hoặc tải Policy Key có sẵn
if not exists(policy_skey_path) or not exists(policy_vkey_path):
payment_key_pair = PaymentKeyPair.generate()
payment_key_pair.signing_key.save(policy_skey_path)
payment_key_pair.verification_key.save(policy_vkey_path)
policy_signing_key = PaymentSigningKey.load(policy_skey_path)
policy_verification_key = PaymentVerificationKey.load(policy_vkey_path)
# Tạo Native Script và tính toán Policy ID
pub_key_policy = ScriptPubkey(policy_verification_key.hash())
policy = ScriptAll([pub_key_policy])
policy_id = policy.hash()
policy_id_hex = policy_id.payload.hex()
native_scripts = [policy]
3. Đóng Gói Metadata CIP-721 và Gửi Giao Dịch
Cấu trúc chuẩn của CIP-721 bắt buộc tuân theo định dạng cây: Key 721 -> Policy ID -> Asset Name -> Các thuộc tính.
Chúng ta sẽ dùng vòng lặp để duyệt qua danh sách tài sản, gắn metadata vào giao dịch (thông qua AuxiliaryData), tính toán Min-ADA và ký giao dịch đa chữ ký.
my_asset = Asset()
my_nft = MultiAsset()
# Khởi tạo khung Metadata CIP-721
metadata = {721: {policy_id_hex: {}}}
for asset in assets:
asset_name = asset["name"]
asset_name_bytes = asset_name.encode("utf-8")
# Cập nhật chi tiết từng NFT vào Metadata
metadata[721][policy_id_hex][asset_name] = {
"name": asset_name,
"type": asset["type"],
"attack": asset["attack"],
"speed": asset["speed"],
"defense": asset["defense"],
"health": asset["health"],
}
nft_name = AssetName(asset_name_bytes)
# LƯU Ý QUAN TRỌNG: Quantity cho NFT LUÔN LÀ 1
my_asset[nft_name] = 1
my_nft[policy_id] = my_asset
# Cấu hình Builder
builder.native_scripts = native_scripts
builder.mint = my_nft
# Gắn Metadata vào Auxiliary Data của giao dịch
auxiliary_data = AuxiliaryData(AlonzoMetadata(metadata=Metadata(metadata)))
builder.auxiliary_data = auxiliary_data
# Tính Min-ADA cho Output chứa nhiều NFT
min_val = min_lovelace(
cardano, output=TransactionOutput(main_address, Value(0, my_nft))
)
# Trả NFT về lại ví của người phát hành
builder.add_output(
TransactionOutput(address=main_address, amount=Value(min_val, my_nft))
)
builder.add_input_address(main_address)
# Ký giao dịch bằng cả Payment Key và Policy Key
signed_tx = builder.build_and_sign(
[payment_skey, policy_signing_key], change_address=main_address
)
# Gửi lên mạng lưới
result = cardano.submit_tx(signed_tx.to_cbor())
print(f"Fee: {signed_tx.transaction_body.fee/1000000} ADA")
print(f"🎉 Mint NFT thành công! Tx ID: {result}")
PHẦN 2: BURN NFTs (ĐỐT TÀI SẢN)
Đôi khi, bạn lỡ đúc sai thông tin NFT hoặc muốn tạo ra cơ chế giảm phát cho dự án của mình bằng cách đốt bỏ NFT. Trên Cardano, việc Burn cực kỳ đơn giản.
Nguyên tắc cốt lõi:
-
Đặt số lượng (Quantity) của Asset thành số âm (Ví dụ:
-1). -
Bạn bắt buộc phải có Policy Signing Key đã dùng để đúc ra NFT đó. Nếu bạn đã lỡ xóa file key này, các NFT đó sẽ tồn tại vĩnh viễn trên blockchain!
Dưới đây là phần mã nguồn để đốt 5 NFT chúng ta vừa đúc ở phần 1 (Phần cấu hình biến môi trường và tải Policy Key giữ nguyên như phần trên, nên mình sẽ đi thẳng vào logic Burn):
# ... (Giữ nguyên phần import, cấu hình ví và tải Policy Key như Phần 1) ...
# Danh sách chính xác các Asset Name cần đốt
assets_to_burn = [
{"name": "Pycardano_test_NFT_001"},
{"name": "Pycardano_test_NFT_002"},
{"name": "Pycardano_test_NFT_003"},
{"name": "Pycardano_test_NFT_004"},
{"name": "Pycardano_test_NFT_005"},
]
my_asset = Asset()
my_nft = MultiAsset()
for asset in assets_to_burn:
asset_name_bytes = asset["name"].encode("utf-8")
nft_name = AssetName(asset_name_bytes)
# LƯU Ý QUAN TRỌNG: Giá trị ÂM để thực hiện hành động Burn
my_asset[nft_name] = -1
my_nft[policy_id] = my_asset
# Cấu hình builder để Burn
builder.native_scripts = native_scripts
builder.mint = my_nft
builder.add_input_address(main_address)
# Ký giao dịch (Bắt buộc phải có Policy Key)
signed_tx = builder.build_and_sign(
[payment_skey, policy_signing_key], change_address=main_address
)
result = cardano.submit_tx(signed_tx.to_cbor())
print(f"Fee: {signed_tx.transaction_body.fee/1000000} ADA")
print(f"🔥 Burn NFT thành công! Tx ID: {result}")
Lời Kết
Xin chúc mừng! Bạn vừa hoàn thành một trong những kỹ năng thú vị nhất trong mảng lập trình Web3: Phát hành và quản lý vòng đời của Non-Fungible Tokens (NFTs). Việc nắm vững cách thiết lập Metadata CIP-721 và cơ chế hoạt động của Policy Key là nền tảng để bạn xây dựng các dự án GameFi hoặc nền tảng sưu tập số trên Cardano.
