Course Content
Pycardano: The Ultimate Course for Python and AI Developers

Bài 4: Code Off-chain với PyCardano – Mint, Update và Burn CIP-68

Chào mừng các bạn đã đi đến Bài 4 của series Thực Chiến CIP-68!

Ở Bài 3, chúng ta đã xây dựng xong hợp đồng thông minh (Smart Contract) bằng Aiken và biên dịch nó thành file plutus.json. Tuy nhiên, Smart Contract tự nó không thể chạy được. Nó chỉ nằm im trên blockchain chờ đợi các giao dịch gửi đến.

Hôm nay, chúng ta sẽ đội chiếc nón “Kỹ sư Off-chain”, sử dụng Python và thư viện PyCardano để xây dựng các kịch bản giao dịch (Transactions) tương tác với hợp đồng đó. Chúng ta sẽ thực thi trọn vẹn 3 vòng đời của một tài sản động: Đúc (Mint) $\rightarrow$ Cập nhật (Update) $\rightarrow$ Đốt (Burn).

Mục Tiêu Bài Học

Kết thúc bài học này, bạn sẽ nắm được:

  1. Cách ánh xạ dữ liệu Datum từ Aiken sang Python (PyCardano).

  2. Xử lý chuẩn xác cơ chế Tiền tố (Prefix) của CIP-68 để sinh ra tên cho Cặp Token.

  3. Viết code giao dịch Mint để sinh ra tài sản CIP-68 và khóa Reference Token.

  4. Viết code giao dịch Update để thay đổi metadata áp dụng cơ chế CKV.

  5. Viết code giao dịch Burn để thiêu hủy đồng bộ cả hai token.

1. Ánh Xạ Cấu Trúc Dữ Liệu (Data Mapping)

Quy tắc tối thượng khi làm việc với Smart Contract: Cấu trúc dữ liệu trên Python phải khớp 100% với định nghĩa trên Aiken.

Trong thư mục offchain, chúng ta sẽ dùng @dataclass của PyCardano để định nghĩa CIP68Metadata và các Action (Redeemer):

from dataclasses import dataclass
from pycardano import PlutusData, Dict

# Ánh xạ cấu trúc Metadata chuẩn CIP-68
@dataclass
class CIP68Metadata(PlutusData):
    CONSTR_ID = 0
    metadata: Dict[bytes, PlutusData]  # Key là bytes, Value là kiểu Data bất kỳ
    version: int                       # Thường mặc định là 1

# Ánh xạ các Action (Mint, Update, Burn)
@dataclass
class Mint(PlutusData):
    CONSTR_ID = 0

@dataclass
class Update(PlutusData):
    CONSTR_ID = 1

@dataclass
class Burn(PlutusData):
    CONSTR_ID = 2

Lưu ý: CONSTR_ID cực kỳ quan trọng, nó phải tương ứng với thứ tự các variant mà bạn đã khai báo trong file .ak ở Bài 2.

2. Xử Lý Tiền Tố (Prefix Mechanism)

Như đã học ở Bài 3.0, CIP-68 yêu cầu tạo ra cặp token có tên khác nhau ở phần tiền tố (Prefix). Dưới đây là cách chúng ta tạo tên token trong Python:

# Lõi của tên token (Ví dụ: "MyDynamicNFT")
token_name_hex = "MyDynamicNFT".encode("utf-8").hex()

# Prefix chuẩn của CIP-68
REF_PREFIX = "000643b0"  # (100) Reference Token
USER_PREFIX = "000de140" # (222) User NFT

# Tính toán Asset Name hoàn chỉnh
ref_asset_name = bytes.fromhex(REF_PREFIX + token_name_hex)
user_asset_name = bytes.fromhex(USER_PREFIX + token_name_hex)

Hai tên này sẽ cùng chia sẻ một Policy ID do Smart Contract của chúng ta quản lý.

3. Giao Dịch Đúc (Mint Transaction)

Trong file demo_mint.py, chúng ta sẽ xây dựng giao dịch đúc. Giao dịch này phải làm 2 việc:

  1. Đúc ra 1 Reference Token và 1 User Token.

  2. Gửi User Token cho người dùng, và gửi Reference Token kèm Inline Datum (chứa metadata) vào địa chỉ của Smart Contract.

# 1. Khởi tạo Metadata
my_metadata = CIP68Metadata(
    metadata={
        b"name": b"My First CIP68",
        b"image": b"ipfs://...",
        b"level": 1,
        b"type": b"Sword"
    },
    version=1
)

builder = TransactionBuilder(context)
builder.add_input_address(user_address) # Lấy UTxO từ ví để trả phí

# 2. Đúc cặp Token
builder.mint = MultiAsset({
    policy_id: Asset({
        ref_asset_name: 1,    # Đúc 1 Reference Token
        user_asset_name: 1    # Đúc 1 User Token
    })
})
# Đính kèm kịch bản đúc (Minting script) và redeemer (Mint Action)
builder.native_scripts = [script]
builder.mint_redeemer = Redeemer(Mint())

# 3. Gửi Reference Token vào Smart Contract kèm Inline Datum
builder.add_output(TransactionOutput(
    address=script_address,
    amount=Value(2_000_000, MultiAsset({policy_id: Asset({ref_asset_name: 1})})),
    datum=my_metadata  # Đính kèm metadata vào đây!
))

# User Token sẽ tự động được trả về ví người dùng dưới dạng Change Output

4. Giao Dịch Cập Nhật (Update Transaction)

Thời gian trôi qua, thanh gươm NFT của bạn đạt đủ điều kiện để nâng lên level 2. Trong file demo_update.py, chúng ta gọi giao dịch Update. Giao dịch này sử dụng cơ chế Continuing Key Validation (CKV).

# 1. Tìm UTxO chứa Reference Token đang nằm trên Smart Contract
target_utxo = find_reference_utxo(script_address, policy_id, ref_asset_name)

# 2. Giải mã metadata cũ và cập nhật thông số mới
old_datum = CIP68Metadata.from_cbor(target_utxo.output.datum.cbor)
old_datum.metadata[b"level"] = 2  # Đổi level 1 -> 2
old_datum.metadata[b"name"] = b"Upgraded Sword"

builder = TransactionBuilder(context)
builder.add_input_address(user_address) # Trả phí giao dịch

# 3. Chi tiêu UTxO từ Smart Contract với Redeemer là Update()
builder.add_script_input(
    utxo=target_utxo,
    script=script,
    redeemer=Redeemer(Update())
)

# 4. CKV: Bắt buộc phải có Continuing Output quay lại Script với Datum mới
builder.add_output(TransactionOutput(
    address=script_address,
    amount=target_utxo.output.amount, # Trả lại nguyên lượng ADA và Ref Token
    datum=old_datum                   # METADATA MỚI ĐÃ ĐƯỢC CẬP NHẬT!
))

# 5. Xác thực chữ ký Admin (Smart contract yêu cầu)
builder.required_signers = [admin_vkey.hash()]

Kỳ diệu chưa? Chỉ vài giây sau khi giao dịch này được xác nhận, nếu người dùng mở ví Nami hay Eternl của họ ra xem, thuộc tính của thanh kiếm sẽ lập tức nhảy lên Level 2 mà không cần động đến ví của họ!

5. Giao Dịch Đốt (Burn Transaction)

Đến một lúc nào đó, bạn muốn hủy bỏ tài sản (ví dụ: vũ khí bị gãy, hoặc thẻ DID hết hạn). Giao dịch Burn (demo_burn.py) phải lấy cả User Token từ ví và Reference Token từ Smart Contract để thiêu hủy đồng thời.

# Tìm UTxO chứa Reference Token trên Script
ref_utxo = find_reference_utxo(script_address, policy_id, ref_asset_name)
# Tìm UTxO chứa User Token trong ví người dùng
user_utxo = find_user_utxo(user_address, policy_id, user_asset_name)

builder = TransactionBuilder(context)
builder.add_input_address(user_address)

# Lấy Reference Token ra khỏi khóa
builder.add_script_input(
    utxo=ref_utxo,
    script=script,
    redeemer=Redeemer(Burn())
)

# Báo với mạng lưới là tôi muốn đốt (mint với số lượng âm)
builder.mint = MultiAsset({
    policy_id: Asset({
        ref_asset_name: -1,   # Đốt Reference Token
        user_asset_name: -1   # Đốt User Token
    })
})
builder.mint_redeemer = Redeemer(Burn())

# Không tạo output quay về script nữa. ADA bị khóa sẽ tự động trả về ví.

Tổng Kết Bài 4

Tuyệt vời! Chúng ta đã hoàn thiện toàn bộ vòng đời của một tài sản CIP-68 ở tầng dưới (Backend/Scripting). Thông qua Bài 4, bạn đã thành thạo:

  • Cách PyCardano giao tiếp với Smart Contract Aiken.

  • Cấu trúc giao dịch sinh ra cặp token (Mint).

  • Sức mạnh của cơ chế CKV trong việc cập nhật Inline Datum mà không can thiệp vào ví người dùng (Update).

  • Logic thu hồi tài sản làm sạch blockchain (Burn).

Tiếp theo là gì? Hiện tại chúng ta vẫn đang chạy các script này qua Terminal (giao diện dòng lệnh). Để mang trải nghiệm này tới tay người dùng cuối (End-users), trong Bài 5, chúng ta sẽ xây dựng một DApp hoàn chỉnh với Frontend (React/Next.js) và Backend tích hợp. Người dùng chỉ cần kết nối ví và bấm nút!

Hẹn gặp lại các bạn ở chặng cuối cùng – Bài 5!
Chi tiết nội source code của bài học các bạn có thể theo dõi tại đây!!!!
CIP-68 Dynamic NFT Implementation

SLOT88
totoslot777
https://nextlevelacademygr.es/
SLOT88
CARVALHO-MANZON Digital Arts
https://kaizen7.pe/
slot maxwin
BANDAR SLOT
SLOT THAILAND
https://www.ui-academy.co.uk/faq/
https://harton.be/
SLOT THAILAND
ANGKATOTO
SLOT THAILAND