
Lập trình PyCardano Bài 6: Hướng Dẫn Đúc (Mint) Fungible Token Bằng Native Script
Xin chào mọi người, chào mừng đã đến với bài học thứ 6 trong chuỗi hướng dẫn lập trình PyCardano!
Nếu ở các bài trước chúng ta chỉ xoay quanh việc chuyển nhận ADA, thì trong bài học ngày hôm nay, chúng ta sẽ bước sang một cảnh giới mới: Tự tay đúc (Mint) đồng tiền điện tử của riêng bạn.
Mục tiêu của bài học hôm nay bao gồm:
-
Hiểu rõ cơ chế phát hành tài sản (mint token) trên mạng lưới Cardano.
-
Thực hành phát hành 100 Fungible Token (FT).
-
Sử dụng Native Script với chính sách đơn giản dựa trên khóa công khai.
-
Triển khai code hoàn chỉnh bằng thư viện PyCardano.
Hãy cùng mở trình soạn thảo code lên và bắt đầu nhé!
1. Import Thư Viện và Cấu Hình Môi Trường (Bước 1, 2, 3)
Đầu tiên, chúng ta cần import các thư viện quen thuộc như os, sys, blockfrost, dotenv và pycardano. Tiếp theo, mã nguồn sẽ đọc các cấu hình mạng và API Key từ file .env để bảo mật thông tin.
Đoạn code dưới đây sẽ tự động kiểm tra: Nếu biến network là “testnet”, nó sẽ trỏ về URL Preprod. Ngược lại, nó sẽ dùng mạng Mainnet.
# ======================================================
# 1. IMPORT THƯ VIỆN CẦN THIẾT
# ======================================================
import os
import sys
from os.path import exists
from blockfrost import ApiError, ApiUrls, BlockFrostApi
from dotenv import load_dotenv
from pycardano import *
# ======================================================
# 2. NẠP BIẾN MÔI TRƯỜNG (.env)
# ======================================================
load_dotenv()
network = os.getenv("BLOCKFROST_NETWORK")
wallet_mnemonic = os.getenv("MNEMONIC")
blockfrost_api_key = os.getenv("BLOCKFROST_PROJECT_ID")
# ======================================================
# 3. CẤU HÌNH MẠNG CARDANO (TESTNET / MAINNET)
# ======================================================
if network == "testnet":
base_url = ApiUrls.preprod.value
cardano_network = Network.TESTNET
else:
base_url = ApiUrls.mainnet.value
cardano_network = Network.MAINNET
2. Thiết Lập Ví Người Dùng và Kết Nối Blockfrost (Bước 4, 5, 6, 7)
Tiếp theo, chúng ta tạo khóa ví từ Mnemonic. Các bạn lưu ý, đây là ví của “Người dùng” (User Wallet). Nó có hai nhiệm vụ: dùng để trả phí mạng (Fee) cho giao dịch và là nơi nhận Token sau khi đúc xong.
Mình cũng thực hiện một query nhỏ gọi đến Blockfrost để kiểm tra xem ví có đủ UTxO (đủ tiền ADA) để thực hiện giao dịch hay không.
# ======================================================
# 4. TẠO KHÓA VÍ TỪ MNEMONIC
# ======================================================
new_wallet = crypto.bip32.HDWallet.from_mnemonic(wallet_mnemonic)
payment_key = new_wallet.derive_from_path("m/1852'/1815'/0'/0/0")
staking_key = new_wallet.derive_from_path("m/1852'/1815'/0'/2/0")
payment_skey = ExtendedSigningKey.from_hdwallet(payment_key)
staking_skey = ExtendedSigningKey.from_hdwallet(staking_key)
# ======================================================
# 5. TẠO ĐỊA CHỈ CARDANO
# ======================================================
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í phát hành & nhận token: {main_address}")
# ======================================================
# 6. KẾT NỐI BLOCKFROST – KIỂM TRA UTxO & SỐ DƯ
# ======================================================
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 sử dụng Faucet để nạp Test ADA.")
sys.exit(1)
else:
print(f"Lỗi Blockfrost: {e}")
sys.exit(1)
total_ada = sum(int(utxo.amount[0].quantity) for utxo in utxos)
print(f"Tổng ADA khả dụng: {total_ada / 1_000_000} ADA")
# ======================================================
# 7. KHỞI TẠO CHAIN CONTEXT
# ======================================================
cardano = BlockFrostChainContext(project_id=blockfrost_api_key, base_url=base_url)
3. Lý Thuyết Cốt Lõi Về Minting Trên Cardano (Bước 8)
Trước khi viết code đúc token, chúng ta bắt buộc phải hiểu lý thuyết này. Vấn đề cốt lõi của bất kỳ blockchain nào là: Khi phát hành token, hệ thống cần biết “Ai là người có quyền đúc (mint) hoặc đốt (burn) token này?”
Cardano giải quyết bài toán này bằng khái niệm Minting Policy (Chính sách đúc tiền). Có 2 cơ chế chính:
-
Native Script: Đơn giản, dựa trên chữ ký, thời gian. Không cần lập trình smart contract phức tạp.
-
Plutus Script: Dùng Smart Contract thực thụ cho các logic phức tạp (DEX, DeFi, Oracle).
Trong bài học hôm nay, chúng ta dùng Native Script vì nó là tiêu chuẩn phổ biến nhất cho việc phát hành Token cơ bản hoặc NFT. Cơ chế hoạt động của nó cực kỳ đơn giản: Giao dịch mint BẮT BUỘC PHẢI CÓ CHỮ KÝ tương ứng với một khóa công khai cụ thể.
Mọi token trên Cardano được định danh bởi một cặp: (Policy ID, Asset Name). Trong đó, Policy ID chính là mã Hash của Native Script này.
Tóm lại: Ai nắm giữ Policy Signing Key, người đó là “Chúa tể” của Token đó, có quyền in thêm hoặc đốt đi. Do đó, khóa Policy phải được bảo mật tuyệt đối!
4. Tạo Policy Key & Native Script (Bước 9, 10)
Bây giờ chúng ta sẽ tạo “Khóa quyền lực” đó. Các bạn lưu ý: Đây KHÔNG phải là payment key của ví, mà là một bộ khóa độc lập chỉ dùng để kiểm soát quyền mint.
Code của chúng ta sẽ tự động kiểm tra xem trong máy đã có thư mục keys và file policy.skey chưa. Nếu chưa có, nó sẽ tạo ra một cặp khóa mới ngẫu nhiên và lưu lại. Sau đó, nó sẽ tính toán ra Policy ID.
# ======================================================
# 9. TẠO HOẶC TẢI POLICY KEY
# ======================================================
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")
if not exists(policy_skey_path) or not exists(policy_vkey_path):
policy_keypair = PaymentKeyPair.generate()
policy_keypair.signing_key.save(policy_skey_path)
policy_keypair.verification_key.save(policy_vkey_path)
policy_signing_key = PaymentSigningKey.load(policy_skey_path)
policy_verification_key = PaymentVerificationKey.load(policy_vkey_path)
# ======================================================
# 10. TẠO NATIVE SCRIPT & LẤY POLICY ID
# ======================================================
# Gắn quyền phát hành vào Verification Key vừa tạo
pub_key_policy = ScriptPubkey(policy_verification_key.hash())
policy = ScriptAll([pub_key_policy])
# Tính toán Policy ID (Mã định danh duy nhất của Token)
policy_id = policy.hash()
policy_id_hex = policy_id.payload.hex()
native_scripts = [policy]
5. Định Nghĩa Token và Số Lượng (Bước 11)
Tại bước này, chúng ta định nghĩa Token muốn đúc. Mình sẽ đặt tên nó là Pycardano_test_COIN_001 và đúc ra đúng 100đồng. (Lưu ý: Nếu số lượng > 0 là Mint, nếu < 0 là Burn).
# ======================================================
# 11. ĐỊNH NGHĨA TOKEN CẦN MINT
# ======================================================
asset_name = "Pycardano_test_COIN_001"
token = AssetName(asset_name.encode("utf-8"))
asset = Asset()
asset[token] = 100 # Số lượng đúc: 100 tokens
# Đóng gói Asset vào MultiAsset với Policy ID tương ứng
multiasset = MultiAsset()
multiasset[policy_id] = asset
6. Xây Dựng Giao Dịch & Tính Toán Min-ADA (Bước 12, 13, 14)
Chúng ta khởi tạo TransactionBuilder và khai báo đầu vào là địa chỉ ví. Điểm khác biệt so với giao dịch gửi tiền bình thường là bạn phải gán builder.native_scripts và builder.mint.
Một lưu ý sống còn ở Bước 13: Trên Cardano, để chống spam, bạn không thể gửi “khống” một token qua lại. Mỗi UTxO chứa token bắt buộc phải kèm theo một lượng ADA tối thiểu (Min-ADA). Chúng ta dùng hàm min_lovelace của PyCardano để tính toán con số này.
# ======================================================
# 12. XÂY DỰNG GIAO DỊCH MINT TOKEN
# ======================================================
builder = TransactionBuilder(cardano)
builder.add_input_address(main_address)
# Cung cấp Script và thông tin Token muốn Mint
builder.native_scripts = native_scripts
builder.mint = multiasset
# ======================================================
# 13. TÍNH MIN-ADA CHO UTxO CHỨA TOKEN
# ======================================================
min_val = min_lovelace(
cardano,
output=TransactionOutput(main_address, Value(0, multiasset))
)
# Kiểm tra xem ví có đủ tiền trả phí mạng + Min-ADA không
if total_ada < min_val + 2_000_000:
print("Không đủ ADA trong ví để thực hiện giao dịch đúc token.")
sys.exit(1)
# Gửi số token vừa đúc kèm theo lượng Min-ADA về lại ví của mình
builder.add_output(
TransactionOutput(main_address, Value(min_val, multiasset))
)
# ======================================================
# 14. THIẾT LẬP TTL (THỜI GIAN SỐNG CỦA GIAO DỊCH)
# ======================================================
builder.ttl = cardano.last_block_slot + 1000
7. Ký (Sign) và Submit Giao Dịch (Bước 15, 16, 17)
Đây là lúc điều kỳ diệu xảy ra! Khi ký một giao dịch mint bằng Native Script, chúng ta CẦN 2 CHỮ KÝ:
-
payment_skey: Chữ ký của chủ ví để xác nhận việc thanh toán phí mạng và sử dụng UTxO đầu vào. -
policy_signing_key: Chữ ký của chủ Policy để chứng minh “Tôi có quyền đúc đồng coin này”.
# ======================================================
# 15. KÝ GIAO DỊCH (VỚI 2 CHÌA KHÓA)
# ======================================================
signed_tx = builder.build_and_sign(
[payment_skey, policy_signing_key],
change_address=main_address
)
# ======================================================
# 16. IN THÔNG TIN TỔNG QUAN
# ======================================================
print("-" * 40)
print(f"Phí giao dịch: {signed_tx.transaction_body.fee / 1_000_000} ADA")
print(f"Mint: 100 {asset_name}")
print(f"Policy ID: {policy_id_hex}")
print("-" * 40)
# ======================================================
# 17. SUBMIT LÊN MẠNG LƯỚI CARDANO
# ======================================================
try:
tx_id = cardano.submit_tx(signed_tx.to_cbor())
print(f"🎉 Mint token thành công! Tx ID: {tx_id}")
print(f"Hãy tra cứu Tx ID trên Cardano Explorer nhé!")
except Exception as e:
print(f"Lỗi khi gửi giao dịch: {e}")
Lời Kết
Vậy là xin chúc mừng! Bằng những dòng code Python ngắn gọn, bạn đã chính thức phát hành thành công một đồng tiền điện tử Fungible Token trên nền tảng blockchain Cardano.
Hãy chạy thử script trên, copy mã Tx ID sinh ra và dán lên trang Cardanoscan (Preprod) để tận mắt chứng kiến thành quả của mình. Ở bài học tiếp theo, chúng ta sẽ ứng dụng kiến thức này để đúc Non-Fungible Token (NFT) – tạo ra những tài sản kỹ thuật số độc nhất vô nhị.
