Workshop 3: Client interaction with common Solana programs (SPLT, Metaplex)

Bài viết này sẽ cung cấp đầy đủ thông tin giúp bạn nắm được cách sử dụng các thư viện phổ biến, hiểu rõ về khái niệm SPL Token và NFT đồng thời giúp bạn tương tác với Smart Contract thông qua thư viện. Tham khảo ngay!

Workshop 3: Client interaction with common Solana programs (SPLT, Metaplex)

SPL TOKEN

Một số khái niệm

  • SPL-Token đại diện cho tất cả các tokens trên mạng Solana. Cả token có thể thay thế (fungible tokens) và không thể thay thế (NFT: fungible non-fungible tokens) trên Solana đều là SPL-Tokens
  • The Token Program chứa các hướng dẫn (instructions) để tạo và tương tác với SPL-Token
  • Token Mint là account giữ dữ liệu về một Token cụ thể, nhưng không nắm giữ Token đó.
  • Token Account là nơi để giữ Token của một Token Mint cụ thể
  • Tạo Token Mint và thuê Token Account để chứa các token trong Solana. Tiền thuê Token Account có thể được hoàn lại khi tài khoản bị đóng, tuy nhiên, Token Mints hiện không thể đóng.

Token Mint

  • Để có thể tạo mới một SPL-Token chúng ta cần tạo Token Mint. Token Mint là account lưu trữ dữ liệu thông tin về một token đặc biệt

Vd: Token SNTR: SENBBKVCM7homnf5RX9zqpf1GFe935hnbU4uVzY1Y6M bạn có thể xem thông tin ở solana explorer hoặc solscan:

  • Để tạo 1 Token Mint, cần gửi 1 transaction instructions đến Token Program. Để làm việc đó, sử dụng hàm createMint từ thư viện @solana/spl-token.
const tokenMint = await createMint(
    connection,
    payer,
    mintAuthority,
    freezeAuthority,
    decimal
);
  • Hàm createMint return publicKey của token mint mới. Và những tham số yêu cầu truyền vào:
  • connection - JSON-RPC kết nối đến cluster
  • payer - public key của người sẽ trả phí cho giao dịch
  • mintAuthority - account được ủy quyền để thực hiện việc mint token
  • freezeAuthority - account được phép đóng băng các token trong. Nếu đóng băng không phải là thuộc tính bạn cần, tham số có thể đặt thành null
  • decimals - số thập phân của token

Token Account

  • Trước khi in ra 1 tokens, cần phải có một nơi để giữ nó gọi là Token Account
  • Token Account giữ các tokens của một loại “mint" riêng và có thuộc “owner" (người sở hữu) riêng. Chỉ người sở hữu account mới có quyền gửi (transfer), đốt (burn), … các tokens đó
  • Sử dụng hàm createAccount từ thư viện `spl-token` để tạo mới 1 Token Account:
const tokenAccount = await createAccount(
    connection,
    payer,
    mint,
    owner,
    keypair
);
  • Hàm createAccount return `publicKey` của Token Account mới. Và những tham số yêu cầu truyền vào:
  • connection - JSON-RPC kết nối đến cluster
  • payer - public key của người sẽ trả phí cho giao dịch
  • mint - token mint mà Token Account sẽ giữ
  • owner - account của người sẽ sở hữu Token Account mới đó
  • keypair - cặp khoá chính. Đây là một tham số tùy chọn để chỉ định địa chỉ Token Account mới. Nếu không có cặp khóa nào được cung cấp thì hàm createAccount sẽ mặc định suy ra từ tài khoản chủ sở hữu (owner) và loại `mint` (mint token) đó.

Associated Token Account

  • Associated Token Account (ATA) là Token Account trong đó địa chỉ của Token Account được lấy bằng khóa công khai của chủ sở hữu và Token Mint. ATA cung cấp một cách xác định để tìm Token Account thuộc sở hữu của một publicKey cụ thể cho một Token Mint
  • Tương tự như trên, bạn có thể tạo ATA được bằng cách sử dụng hàm createAssociatedTokenAccount của thư viện `spl-token`.
const associatedTokenAccount = await createAssociatedTokenAccount(
    connection,
	payer,
	mint,
	owner,
);
  • Hàm createAssociatedTokenAccount return `publicKey` của ATA mới. Và những tham số yêu cầu truyền vào:
  • connection - JSON-RPC kết nối đến cluster
  • payer - public key của người sẽ trả phí cho giao dịch
  • mint - token mint mà Token Account sẽ giữ
  • owner - tài khoản của người sẽ sở hữu Token Account mới đó

Bạn cũng có thể sử dụng getOrCreateAssociatedTokenAccount để lấy ATA với một địa chỉ nhất định hoặc tạo nó nếu nó không tồn tại. Ví dụ: nếu bạn đang muốn airdrop cho một người dùng nhất định, bạn có thể sử dụng chức năng này để đảm bảo rằng ATA tồn tại và được tạo nếu nó chưa tồn tại.

Mint Tokens

  • Minting token là quá trình phát hành các token mới vào lưu thông. Khi mint (đúc) mã thông báo, tăng nguồn cung (supply) của token mint và gửi các tokens mới được đúc vào Token Account. Chỉ mintAuthority mới có quyền đúc mã thông báo mới.
  • Sử dụng hàm `mintTo` của thư viện `spl-token` để mint tokens.
const transactionSignature = await mintTo(
    connection,
    payer,
    mint,
    destination,
    authority,
    amount
);

Hàm mintTo return `TransactionSignature` và bạn có thể xem ở Solana explorer. Và những tham số yêu cầu truyền vào:

  • connection - JSON-RPC kết nối đến cluster
  • payer - public key của người sẽ trả phí cho giao dịch
  • mint - token mint mà Token Account sẽ giữ
  • destination - Token Account mà bạn muốn mint đến
  • authority - account có quyền mint token đó
  • amount - số lượng tokens sẽ được mint

Transfer Tokens

  • Chuyển SPL-Token yêu cầu cả người gửi và người nhận phải có Token Account. Các mã thông báo được chuyển từ Token Account của người gửi sang Token Account của người nhận.
  • Có thể sử dụng getOrCreateAssociatedTokenAccount khi lấy tài Token Account được liên kết của người nhận để đảm bảo Token Account của họ tồn tại trước khi chuyển. Nếu Token Account chưa tồn tại, chức năng này sẽ tạo nó và người thanh toán trong giao dịch sẽ được ghi nợ các loại đèn cần thiết để tạo tài khoản.
  • Sử dụng hàm `transfer` của thư viện `spl-token` để mint tokens

const transactionSignature = await transfer(
    connection,
    payer,
    source,
    destination,
    owner,
    amount
)

Burn Tokens

  • Đốt mã thông báo (Burn Tokens) là quá trình giảm nguồn cung cấp Tokens của Token Mint nhất định. Đốt mã thông báo sẽ xóa chúng khỏi Token Account đã được gửi và khỏi nguồn cung đang lưu hành.
  • Sử dụng hàm `burn` của thư viện `spl-token` để đốt tokens:

const transactionSignature = await burn(
    connection,
    payer,
    account,
    mint,
    owner,
    amount
)

  • Hàm burn trả về TransactionSignature mà bạn có thể xem trên Solana Explorer. Và những tham số yêu cầu truyền vào:
  • connection - JSON-RPC kết nối đến cluster
  • payer - public key của người sẽ trả phí cho giao dịch
  • account - token account bạn muốn burn
  • mint - token mint mà Token Account thuộc account
  • owner - địa chỉ của người sở hữu token account đó
  • amount - số lượng tokens sẽ được burn

Demo

Tiếp đó, chúng ta sẽ cùng demo các hàm như create Token Mint, create Token Accounts, mint tokens, transfer tokens và burn tokens.

1. Khởi tạo project

  • Dùng lệnh bên dưới để tạo project template:

npx create-solana-client [INSERT_PROJECT_NAME_HERE]

  • Sau đó cần cài đặt dependency `@solana/spl-token`

npm install @solana/spl-token

2. Tạo Token Mint

  • Sử dụng thư viện `spl-token` khai báo ở đầu file index.ts

import * as token from '@solana/spl-token'

  • Xây dựng hàm `createNewMint`

async function createNewMint(
    connection: web3.Connection,
    payer: web3.Keypair,
    mintAuthority: web3.PublicKey,
    freezeAuthority: web3.PublicKey,
    decimals: number
): Promise<web3.PublicKey> {

    const tokenMint = await token.createMint(
        connection,
        payer,
        mintAuthority,
        freezeAuthority,
        decimals
    );

    console.log(
        `Token Mint: https://explorer.solana.com/address/${tokenMint}?cluster=devnet`
    );

    return tokenMint;
}

3. Tạo Token Account


async function createTokenAccount(
    connection: web3.Connection,
    payer: web3.Keypair,
    mint: web3.PublicKey,
    owner: web3.PublicKey
) {
    const tokenAccount = await token.getOrCreateAssociatedTokenAccount(
        connection,
        payer,
        mint,
        owner
    )

    console.log(
        `Token Account: https://explorer.solana.com/address/${tokenAccount.address}?cluster=devnet`
    )

    return tokenAccount
}

4. Mint Tokens

async function mintTokens(
    connection: web3.Connection,
    payer: web3.Keypair,
    mint: web3.PublicKey,
    destination: web3.PublicKey,
    authority: web3.Keypair,
    amount: number
) {
    const transactionSignature = await token.mintTo(
        connection,
        payer,
        mint,
        destination,
        authority,
        amount
    )

    console.log(
        `Mint Token Transaction: https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`
    )
}

5. Transfer tokens


async function transferTokens(
    connection: web3.Connection,
    payer: web3.Keypair,
    source: web3.PublicKey,
    destination: web3.PublicKey,
    owner: web3.Keypair,
    amount: number
) {
    const transactionSignature = await token.transfer(
        connection,
        payer,
        source,
        destination,
        owner,
        amount
    )

    console.log(
        `Transfer Transaction: https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`
    )
}

6. Burn Tokens


async function burnTokens(
    connection: web3.Connection,
    payer: web3.Keypair,
    account: web3.PublicKey,
    mint: web3.PublicKey,
    owner: web3.Keypair,
    amount: number
) {
    const transactionSignature = await token.burn(
        connection,
        payer,
        account,
        mint,
        owner,
        amount
    )

    console.log(
        `Burn Transaction: https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`
    )
}

7. Chạy chương trình

async function main() {
    const connection = new web3.Connection(web3.clusterApiUrl("devnet"))
    const user = await initializeKeypair(connection)

    const mint = await createNewMint(
        connection,
        user,
        user.publicKey,
        user.publicKey,
        2
    )

    const tokenAccount = await createTokenAccount(
        connection,
        user,
        mint,
        user.publicKey
    )

    await mintTokens(connection, user, mint, tokenAccount.address, user, 100)

    const receiver = web3.Keypair.generate().publicKey
    const receiverTokenAccount = await createTokenAccount(
        connection,
        user,
        mint,
        receiver
    )

    await transferTokens(
        connection,
        user,
        tokenAccount.address,
        receiverTokenAccount.address,
        user,
        50
    )

    await burnTokens(connection, user, tokenAccount.address, mint, user, 25)
}

METAPLEX - NFT

NFT là gì?

  • NFT (viết tắt là Non-fungible token - tài sản không thể thay thế được) là một token mà người dùng có thể sở hữu và liên kết đến một số phần dữ liệu như ảnh, âm thanh, video, …
  • Điều làm cho token NFT khác với các token khác là mỗi NFT có một mã định danh độc nhất cho phép chủ sở hữu chứng minh rằng token của họ là duy nhất.
  • Lấy ví dụ:

Nhà hoạ sĩ Picasso muốn tạo bộ sưu tập (collection) gồm 5 bức tranh mà mỗi bức tranh là duy nhất vì nó được vẽ bởi chính tay ông ấy bằng bút chì và được xem là sẽ không có bản sao nào giống như thế được tạo ra. Picasso đã vẽ và ký tên lên nó. Như vậy, bạn có thể mua bức tranh bản gốc đó và lấy được chứng nhận từ thư viện bảo tàng là bạn đã có được bức tranh gốc từ Picasso.

  • Ví dụ trên với NFT:

Picasso muốn tạo một bộ sưu tập gồm năm bức tranh của mình, trong đó mỗi bức tranh là một bức tranh kỹ thuật số giống như một tệp PNG. Picasso sẽ tạo 5 NFT cho mỗi bức tranh và cung cấp một mã định danh duy nhất cho mỗi bức tranh. Khi đã sẵn sàng ông sẽ ra mắt và dùng ví của mình để ký cho bộ sưu tập NFT đó. Khi đã đưa nó lên công khai (public), mọi người trong mạng blockchain có thể thấy được bộ sưu tập đó và ông muốn bán nó thì chỉ cần chuyển bức tranh NFT nó đến địa chỉ của người muốn mua nó. Người mua bức tranh NFT đã nhận được bức tranh NFT gốc đó từ chính Picasso và họ nó thể chứng minh đó là một bức tranh NFT có một không hai do nó định danh là duy nhất.

NFT được tạo ra như thế nào?

  • NFT được đại diện trên Solana dưới dạng SPL-Token với tài khoản siêu dữ liệu được liên kết (metadata account), số thập phân (decimals) là 0 và nguồn cung cấp tối đa (max supply) là 1.
  • Thông thường các NFT metadata có cả thành phần on-chain và off-chain. Phần on-chain metadata được lưu trong account liên kết với token mint và chứa thuộc tính URI trỏ đến file JSON ở off-chain. Thành phần off-chain lưu trữ dữ liệu bổ sung và liên kết đến nội dung ví dụ như là hình ảnh. Hệ thống lưu trữ dữ liệu vĩnh viễn như Arweave thường được sử dụng để lưu các nội dung off-chain của NFT metadata. Dưới đây là ví dụ về mối quan hệ giữa metadata ở on-chain và off-chain. Dữ liệu metadata on-chain chứa một trường URI trỏ đến off-chain json file chứa link đến hình ảnh của NFT và các thông tin bổ sung.
Metaplex là một tập hợp các công cụ giúp đơn giản hóa việc tạo và phân phối NFT trên chuỗi khối Solana.
  • Ở Solana, chúng ta có thể create, upload, mint, … NFT với Metaplex. Metaplex là một tiêu chuẩn NFT trên Solana và đã tạo ra một bộ công cụ, thư viện hoá tiêu chuẩn để tạo NFT. Với Metaplex chúng ta không cần phải viết Solana Program để  triển khai NFT, Metaplex đã triển khai các chương trình NFT tiêu chuẩn của riêng mình và bất kỳ nhà phát triển nào cũng có thể tương tác, xây dựng các bộ sưu tập NFT của riêng họ. Cụ thể là Candy Machine Program. Candy Machine Program là một Solana Program được thiết kế để giao dịch NFT cho người dùng.
  • Bạn có thể khám phá thêm nhiều thông tin, ví dụ, tools, … của Metaplex về NFT ở github: https://github.com/metaplex-foundation/metaplex

NFT với Metaplex

Candy Machine v2 đơn giản hóa quá trình tạo NFT để bạn có thể tập trung vào điều quan trọng: metadata và nội dung NFT. Tạo NFT bao gồm:

  • Chuẩn bị nội dung và metadata
  • Cấu hình candy machine
  • Tải lên candy machine

1. Chuẩn bị nội dung và metadata:

  • Trước khi tạo một collection, cần chuẩn bị các nội dung và metadata cho collection. Metadata là một JSON file chứa các thuộc tính:
  • name - tên của NFT sẽ hiển thị
  • symbol - biểu tượng tùy chọn của NFT sẽ hiển thị trên chuỗi
  • description - mô tả của NFT
  • seller_fee_basis_points - phí thu khi bán NFT được chia cho những người tạo
  • image - tên tệp của hình ảnh tương ứng mà NFT sẽ hiển thị.
  • attributes - một mảng các thuộc tính cho NFT trong đó mỗi mục trong mảng là `trait_type` và `value`
  • properties - một đối tượng với các `files` và `creators`. `files` là danh sách các tệp đi kèm với metadata và `creators` là danh sách các đối tượng đại diện cho người tạo NFT và phần chia sẻ phí người bán của họ. Đây là một ví dụ:
{
    "files": [{ "uri": "some-image.png", "type": "image/png" }],
    "creators": [
        {
            "address": "8T9YDcm5zFMRNiUS4aohgVxkqCNAFbnvvzBbxBzkRFck",
            "share": 100
        }
    ]
}
  • collection - một đối tượng có `name` và `family` đại diện cho tên và họ của collection NFT.

Bạn có thể tham khảo thêm tại đây để biết thêm chi tiết và ý nghĩa của Token Standard: https://docs.metaplex.com/programs/token-metadata/token-standard

Metadata file có định dạng như dưới:

{
    "name": "Number #0001",
    "symbol": "NB",
    "description": "Collection of 10 numbers on the blockchain. This is the number 1/10.",
    "seller_fee_basis_points": 500,
    "image": "0.png",
    "attributes": [
        {"trait_type": "Layer-1", "value": "0"},
        {"trait_type": "Layer-2", "value": "0"},
        {"trait_type": "Layer-3", "value": "0"},
        {"trait_type": "Layer-4", "value": "1"}
    ],
    "properties": {
        "creators": [{"address": "N4f6zftYsuu4yT7icsjLwh4i6pB1zvvKbseHj2NmSQw", "share": 100}],
        "files": [{"uri": "0.png", "type": "image/png"}]
    },
    "collection": {"name": "numbers", "family": "numbers"}
}

Bạn có thể tham khảo thêm tại link: https://docs.metaplex.com/deprecated/candy-machine-js-cli/preparing-assets

2. Cấu hình Candy Machine:

  • Với metadata đã chuẩn bị, bạn có thể sử dụng Candy Machine v2 để cấu hình và tạo "Candy Machine" NFT của mình.
  • Cấu hình thực hiện thông qua một tệp JSON. Khi bạn sử dụng Candy Machine v2, JSON trong tệp cấu hình này được lưu trữ trong một account trên chuỗi nơi các trường cấu hình có thể được cập nhật bằng Candy Machine CLI. Các thuộc tính trong tệp cấu hình này là:

{
    "price": 1,
    "number": 5,
    "gatekeeper": null,
    "solTreasuryAccount": "8T9YDcm5zFMRNiUS4aohgVxkqCNAFbnvvzBbxBzkRFck",
    "splTokenAccount": null,
    "splToken": null,
    "goLiveDate": "25 Dec 2021 00:00:00 GMT",
    "endSettings": null,
    "whitelistMintSettings": {
        "mode": { "burnEveryTime": true },
        "mint": "CNDCM9RsbUcjX4V12wgJ6QjLyjvRyzyWwLu8eLKh6aSu",
        "presale": true,
        "discountPrice": 0.01
    },
    "hiddenSettings": null,
    "storage": "arweave",
    "ipfsInfuraProjectId": null,
    "ipfsInfuraSecret": null,
    "awsS3Bucket": null,
    "nftStorageKey": null,
    "noRetainAuthority": false,
    "noMutable": false
}

Bạn có thể xem thông tin chi tiết về các trường tại https://docs.metaplex.com/deprecated/candy-machine-js-cli/configuration

3. Upload candy machine:

  • Sau khi chuẩn bị xong nội dung cho bộ sưu tập và tệp cấu hình “Candy Machine”, bước tiếp theo là tạo “Candy Machine". Điều này được thực hiện bằng cách sử dụng command upload lên từ Candy Machine CLI. Lệnh tải lên thực hiện hai việc:
  • Tải tệp nội dung và tạo tệp .cache
  • Tạo một tài khoản on-chain được gọi là Candy Machine tạm thời để lưu trữ các liên kết đến bộ sưu tập đã tải lên và tương ứng với tệp .cache. Sau đó, tài khoản “Candy Machine" on-chain sẽ được sử dụng để phân phối bộ sưu tập khi hoạt động mint được diễn ra.

Sau khi chạy lệnh upload, cách tốt nhất là bạn cũng nên chạy lệnh verify_upload. Lệnh này sẽ kiểm tra xem mỗi mục nhập trong tệp .cache có khớp với tài khoản “candy machaine" on-chain hay không. Nếu bất kỳ mục nhập URI nào không khớp với thông tin được lưu trữ on-chain, bạn sẽ cần chạy lại lệnh upload.

4. Quá trình mint NFT:

  • Khi quá trình upload đã được xác minh thành công, “candy machine” đã sẵn sàng để mint token. Cài đặt cấu hình “candy machine” sẽ xác định cách các mã thông báo mint ra. Minting thường được thực hiện thông qua giao diện người dùng.
  • Bạn có thể đúc mã thông báo trực tiếp từ Candy Machine v2 CLI bằng cách sử dụng lệnh `mint_one_token`

Demo

Khởi tạo project

  • Đầu tiên hãy tải https://github.com/Unboxed-Software/solana-metaplex-intro
  • Thư mục code trên chứa folder `assets` và tệp cấu hình candy machine sẵn `config.json`. Ngoài ra, nó bao gồm hai scripts trợ giúp: tạo một cặp khóa mới và cái còn lại whitelist token mà sau chúng ta sẽ sử dụng để kích hoạt cài đặt whitelist.

Cài đặt

Vào thư mục root của project đã tải về và chạy lệnh:


npm install

  • Sau đó, chạy lệnh bên dưới để tạo ra một cặp khoá `keypair` để sử dụng và airdrop sol đến ví này:

npm start

  • Tiếp đến, bạn đã có 1 file `private-key.json`. Copy private key từ file đó và nhập vào ví Phantom.

Link hình:

https://soldev.app/assets/solana-nft-phantom.gif

Cài đặt metaplex

Trong thư mục root của project, clone source code metaplex bên dưới:


git clone https://github.com/metaplex-foundation/deprecated-clis

Repository này chứa Candy Machine v2 CLI

  • Tiếp đó, chúng ta sử dụng `ts-node` để chạy lệnh. Sử dụng lệnh bên dưới để cài đặt:

npm install -g typescript
npm install -g ts-node

  • Cài đặt các Metaplex dependencies:

yarn install --cwd deprecated-clis

  • Kiểm tra Candy Machine v2 version:

ts-node deprecated-clis/src/candy-machine-v2-cli.ts --version

Cấu hình Candy Machine

  • Mở thư mục `config.json` ở bên trong root của project. Cấu hình như bên dưới:

{
    "price": 1,
    "number": 5,
    "gatekeeper": null,
    "solTreasuryAccount": "<YOUR_WALLET_ADDRESS>",
    "splTokenAccount": null,
    "splToken": null,
    "goLiveDate": "25 Dec 2020 00:00:00 GMT",
    "endSettings": null,
    "whitelistMintSettings": null,
    "hiddenSettings": null,
    "storage": "arweave",
    "ipfsInfuraProjectId": null,
    "ipfsInfuraSecret": null,
    "awsS3Bucket": null,
    "nftStorageKey": null,
    "noRetainAuthority": false,
    "noMutable": false
}

  • Thay đổi `solTreasuryAccount` chính là wallet address của bạn đã tạo ở trên
  • Điểm qua một số thuộc tính:
  • `price`: 1, nghĩa là NFT có giá 1 SOL
  • `number`: 5, nghĩa là bộ sưu tập này gồm 5 NFTs
  • `goLiveDate`: để một thời gian trong quá khứ để chúng ta có thể mint khi upload chúng
  • `storage`: `arweave` máy chủ lưu metadata trong 7 ngày. Và nếu main-net thì chúng ta sử dụng: `arweave-sol` để lưu dữ liệu dài hạn, vĩnh viễn.
  • `noRetainAuthority`: false, để người mint NFT không thể thay đổi metadata
  • `noMutable`: false, để chúng ta có thể update được metadata nếu cần thiết

Chuẩn bị nội dung, tài sản NFT

  • Trong thư mục root của project có folder `assets` lưu trữ các tệp hình ảnh, metadata chúng ta cần sử dụng. Mở tệp: `0.json` và điền vào creator trường `address` là địa chỉ ví chúng ta đã tạo bên trên.

{
    "name": "1",
    "symbol": "TEST",
    "image": "0.png",
    "properties": {
        "files": [{ "uri": "0.png", "type": "image/png" }],
        "category": "image",
        "creators": [
            {
                "address": "<YOUR_WALLET_ADDRESS>",
                "share": 100
            }
        ]
    },
    "description": "",
    "seller_fee_basis_points": 500,
    "attributes": [
        { "trait_type": "background", "value": "blue" },
        { "trait_type": "eyes", "value": "star-eyes" },
        { "trait_type": "mouth", "value": "triangle-mouth" },
        { "trait_type": "face", "value": "teal-face" }
    ],
    "collection": { "name": "NAME", "family": "FAMILY" }
}

Và bạn cập nhật trường `address` của các tệp định dạng `.json` còn lại để sẵn sàng cho việc `verify_assets` và upload.

Command `verify_assets` sẽ kiểm tra:

  • Định dạng files hỗ trợ
  • Kiểm tra các files `.json` metadata với các hình ảnh tương ứng
  • Trường `creators` trong các files `.json`

ts-node deprecated-clis/src/candy-machine-v2-cli.ts verify_assets ./assets

Kết quả như hình bên dưới:

Tạo Candy Machine:

  • Chúng ta sẽ cấu hình Candy Machine và xác minh nội dung của chúng ta rồi sẵn sàng để tải lên (upload) bằng sử dụng lệnh:
ts-node deprecated-clis/src/candy-machine-v2-cli.ts upload \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json \
    -cp config.json \
    -c example \
    ./assets

Kết quả như bên dưới:

Trong folder .cache, xuất hiện file devnet-example.json chứa các thông tin của Candy Machine và các link Arweave tương ứng với mỗi metadata của NFT. Khi chúng ta sử dụng lệnh upload, hình ảnh và metadata được upload đến máy chủ Arweave để sẵn sàng cho việc tạo NFT. Mỗi Arweave link đại diện cho một thành phần off-chain của mỗi NFT metadata.

  • Tệp devnet-example.json có định dạng như dưới:
{
    "program": {
        "uuid": "GSSEGg",
        "candyMachine": "GSSEGgeCvndhjd3GGNs8VM65qHPAiBF8cF2EWTZ3bm8k",
        "collection": "4cXtoUxF9C3nYxo31Hku6gFYDEcnidqSScbctfY17HZr"
    },
    "items": {
        "0": {
            "link": "https://arweave.net/nk7VSghTt9iP6cVYnVxo4SAN_t6RPxE6iESP-Z49s4s",
            "imageLink": "https://arweave.net/8Y9i5mvF3u_3XHzSAfiDxt8pUubGB8HwXTTEnD4Kp7c?ext=png",
            "name": "1",
            "onChain": true,
            "verifyRun": false
        },
        "1": {
            "link": "https://arweave.net/LEbsEUVr-oMCgVkZlvSEmOoBRR7QcoFG-won6tZD16g",
            "imageLink": "https://arweave.net/T78LQZsteVWK83g49wy-Lt3cig3w8N1kAXyZiU5Pxdg?ext=png",
            "name": "2",
            "onChain": true,
            "verifyRun": false
        },
        "2": {
            "link": "https://arweave.net/0D-klGK9bceLNm0Bxo4ua59RlLq1i0cM1A31Smsgvwo",
            "imageLink": "https://arweave.net/GbPL0RTX_IEJvbcAEwqGI2nyzsT6IXRHDgk_o_fdwJQ?ext=png",
            "name": "3",
            "onChain": true,
            "verifyRun": false
        },
        "3": {
            "link": "https://arweave.net/hz0lrDqCvtVnvToHjmSCDlSF7feCcu8KX_zJDq_BBRc",
            "imageLink": "https://arweave.net/mhxIIf-U4xwA1yFouxZx1eUmpMOr-7gz9a377fwbO8w?ext=png",
            "name": "4",
            "onChain": true,
            "verifyRun": false
        },
        "4": {
            "link": "https://arweave.net/MQsmrTW6BIufImiDjHY3py3dM1w8DdowKGtJ307MQW4",
            "imageLink": "https://arweave.net/otRFxnPyLW_fmbUvskf4DE63n8dUurHt5Qm--gWWOdA?ext=png",
            "name": "5",
            "onChain": true,
            "verifyRun": false
        }
    },
    "env": "devnet",
    "cacheName": "example"
}

Bước tiếp theo chúng ta sẽ verify_upload để xác nhận 1 lần nữa các thành phần trong devnet-example.json đã được upload thành công và đúng với URI lưu trên on-chain chưa. Sử dụng lệnh:


ts-node deprecated-clis/src/candy-machine-v2-cli.ts verify_upload \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json \
    -c example

Kết quả sẽ như bên dưới:


Sau đó chúng ta sẽ mint 1 NFT từ Candy Machine sử dụng lệnh mint_one_token:


ts-node deprecated-clis/src/candy-machine-v2-cli.ts mint_one_token \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json \
    -c example

Sau đó quay lại ví Phantom để kiểm tra xem đã mint thành công chưa bằng việc click chọn icon như hình:

Candy Machine UI

  • Bạn có thể sử dụng Candy Machine UI để mint 1 NFT
  • Đầu tiên, bạn cần clone repository của Metaplex:

git clone https://github.com/metaplex-foundation/candy-machine-ui

Sau đó, đổi tên file .env.example thành .env

  • Thay đổi thông tin REACT_APP_CANDY_MACHINE_ID để khớp với ID của Candy Machine đã tạo bên trên. Có thể tìm thấy bên trong Candy Machine cache - tệp này nằm trong cùng thư mục mà bạn đã thực thi lệnh upload (ví dụ: devnet-example.json)

REACT_APP_CANDY_MACHINE_ID=<YOUR CANDY MACHINE PROGRAM ID>

REACT_APP_SOLANA_NETWORK=devnet
REACT_APP_SOLANA_RPC_HOST=https://metaplex.devnet.rpcpool.com/

Trong thư mục candy-machine-ui, chạy lệnh:


yarn install && yarn start

Mở trình duyệt, nhập link: localhost:3000 và click “MINT" để tạo NFT từ candy machine.

Bật Gatekeeper:

  • Sau khi giao diện candy machine hoạt động, chúng ta tiếp tục cập nhật cài đặt gatekeeper.
  • Mở file config.json và cập nhật trường gatekeeper để  bật CAPTCHA:

 "gatekeeper": {
        "gatekeeperNetwork": "ignREusXmGrscGNUesoU9mxfds9AiYTezUKex2PsZV6",
        "expireOnUse": true
    }

Để cập nhật candy machaine, sử dụng lệnh update_candy_machine:


ts-node deprecated-clis/src/candy-machine-v2-cli.ts update_candy_machine \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json \
    -cp config.json \
    -c example

Mở trình duyệt link: localhost:3000 và click vào nút “MINT". Bây giờ nó sẽ yêu cầu xác nhận CAPTCHA trước khi mint NFT.

Bật Whitelist:

  • Tiếp theo đó chúng ta sẽ bật cài đặt whitelist
  • Đầu tiên trong phần khởi tạo project có sẵn tệp và script giúp ta tạo whitelist token. Script này giúp ta dễ dàng:
  • Tạo 1 token mint
  • Tạo 1 account mới
  • Mint tokens đến account mới đó và in địa chỉ của whitelist token đó để chúng ta sử dụng
  • Whitelist token sẽ được mint từ ví chúng ta đang sử dụng trong suốt bài này. Trong thư mục root của project ban đầu, chạy lệnh:

npm run whiteListToken

Tiếp đó, trong file config.json, sửa lại trường `gatekeeper` thành `null`


"gatekeeper": null

Cập nhật goLiveDate thành 1 ngày trong tương lai:


"goLiveDate": "25 Dec 2050 00:00:00 GMT"

  • Cập nhật whitelistMintSettings: gán giá trị trường mint là địa chỉ mint của whitelist token chúng ta đã tạo ở trên:

   "whitelistMintSettings": {
        "mode": { "burnEveryTime": true },
        "mint": "<WHITELIST_TOKEN_ADDRESS>",
        "presale": true,
        "discountPrice": 0.01
    }

  • Sau đó cập nhật lại candy machine, sử dụng lệnh:

ts-node deprecated-clis/src/candy-machine-v2-cli.ts update_candy_machine \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json \
    -cp config.json \
    -c example

  • Refresh lại trang localhost:3000 và mint NFT từ ví chứa whitelist token

Rút phí thuê

Sau khi đã mint hoàn tất, chúng ta có thể rút phí thuê bằng cách sử dụng lệnh bên dưới và thay <candy_machine_id> bằng địa chỉ candy machine từ tệp devnet-example.json:


ts-node deprecated-clis/src/candy-machine-v2-cli.ts withdraw <candy_machine_id> \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json

Signing NFTs

  • Cuối cùng, chúng ta ký NFTs để xác thực bạn là người tạo ra bộ sưu tập NFT đó bằng lệnh sign_all:

ts-node deprecated-clis/src/candy-machine-v2-cli.ts sign_all \
    -r, --rpc-url https://metaplex.devnet.rpcpool.com\
    -k ./private-key.json \
    -c example

Lấy thông tin của NFT

Bạn có thể sử dụng thư viện SDK metaplex-foundation để lấy thông tin của NFT:

https://docs.metaplex.com/sdks/js/

Code tham khảo:

import { Metaplex } from "@metaplex-foundation/js";
import { Connection, clusterApiUrl, PublicKey, Cluster } from "@solana/web3.js";

const cluster = process.env.cluster as Cluster;
const connection = new Connection(clusterApiUrl(cluster));
const metaplex = Metaplex.make(connection);

async function findByMint(mintAddress: string) {
 const nft = await metaplex
   .nfts()
   .findByMint({
     mintAddress: new PublicKey(mintAddress)
   })
   .run();
 return nft;
}

async function main() {
 console.log(
   "NFT: ",
   await findByMint("DphLEnHq8pBQDMmajUEdq7fiuF3bm7XzMiwDC9pvVdRM")
 );
}

main()
 .then(() => {
   console.log("Finished successfully");
   process.exit(0);
 })
 .catch((error) => {
   console.log(error);
   process.exit(1);
 });

Kết quả:

Tài liệu tham khảo

1. Solana Cookbook phiên bản Tiếng Việt: https://solanacookbook.com/vi

2. Coding Camp Blogs: https://codingcamp.so/blogs

3. React: https://reactjs.org/

4. Ant Design: https://ant.design/

5. @solana/web3.js: https://solana-labs.github.io/solana-web3.js/

6. Solana Token Program Library: https://solana-labs.github.io/solana-program-library/token/js/

7. Token Program Course: Token Program Course

8. React + Solana Template: https://github.com/dmcong/react-solana-template/tree/lesson-3

Các bài viết liên quan

Workshop 1: Introduction to Solana

Workshop 2: Xây dựng giao diện tương tác với Solana

Về Sentre Protocol

Sentre Protocol là nền tảng mở All-in-One hoạt động trên mạng lưới Solana, cung cấp DApp Store và giao thức mở giúp tích lũy thanh khoản. Với Sentre:

  • Người dùng có thể cài đặt các DApp yêu thích của mình và trải nghiệm thế giới DeFi trên một nền tảng duy nhất;
  • Lập trình viên và đối tác có thể phát hành DApp thông qua Sen Store, tận dụng tài nguyên có sẵn và tự do đóng góp cho nền tảng.