Để tiếp tục với series bài viết về Blockchain Solana thì hôm nay mình sẽ giới thiệu và hướng dẫn các bạn Xây dựng Backend API để mint NFT trên Solana Network bằng NodeJS phần 2.
Bài viết sẽ gồm các 2 phần:
- Phần 1:
- Thiết kế hệ thống
- Dựng hệ thống Backend API bằng Nodejs
- Build và deploy SmartContract để mint NFT
- Phần 2:
- Tạo tài khoản ipfs
- Viết API kết nối SmartContract
- Code logic thực hiện mint NFT
Trước khi bắt đầu phần 2, các bạn có thể xem lại phần 1 ở đây
Tạo tài khoản ipfs
IPFS là viết tắt của từ Interplanetary File System, một hệ thống tập tin phân tán ngang hàng kết nối tất cả các thiết bị máy tính với nhau. Cụ thể hơn, nó sẽ phân phối dữ liệu được lưu trữ theo hình thức P2P, hay còn gọi là mạng ngang hàng (mạng đồng đẳng).
Cách hoạt động của IPFS: Đầu tiên mọi dữ liệu sẽ được mã hoá và được lưu dưới dạng mã hash (còn gọi là đối tượng IPFS). Cách thức hoạt động của IPFS sẽ tương tự như BitTorrent, đồng nghĩa với mỗi máy tính tham gia trong mạng lưới của nó sẽ đảm nhận cả việc download lẫn upload dữ liệu mà không cần có sự có mặt của một máy chủ trung tâm. Tổng quan, cách hoạt động của IPFS sẽ có 2 phần chính:
- Xác định tệp có địa chỉ nội dung (giá trị hash của tệp đó).
- Tìm dữ liệu được lưu trữ và tải xuống: khi bạn có đoạn hash của file hay trang cần tải, mạng sẽ tìm và connect tới máy tốt nhất để tải dữ liệu xuống cho bạn.
Các bạn đăng ký tài khoản và tạo project tại đây: https://infura.io/register. Sau khi tạo project thành công, có thể truy cập trang quản lý:
Sau khi có tài khoản, các bạn tạo mới project để lấy các thông tin key phục vụ việc kết nối để upload ảnh khi mint NFT:
Sau khi tạo project, sẽ có thông tin của PROJECT ID, API KEY SECRET và IPFS API ENDPOINT:
Viết API kết nối SmartContract và thực hiện mint NFT
Sau khi đã có ProgramId của smart-contract, chúng ta sẽ viết 1 API ở backend NodeJS để mint NFT. Về vấn đề thêm route, validate, authenticate cho API, mình sẽ viết trong source code ở: https://github.com/gmo-vietnamlab/nft-certificate-backend. Trong bài viết này, mình chỉ tập trung vào phần logic API.
Bước 2: Copy file idl
Copy file idl được tạo ở phần 1 vào folder: vnlab-nft-certificate-backend/src/target/idl/sol_mint_nft.json
Bước 3: Viết code controller
Tạo file vnlab-nft-certificate-backend/src/controllers/mint-nft-sol.controller.js và thêm code sau:
const catchAsync = require('../utils/catchAsync');
const anchor = require("@project-serum/anchor");
const ipfsClient = require("ipfs-http-client");
const {
TOKEN_PROGRAM_ID,
MINT_SIZE,
createAssociatedTokenAccountInstruction,
getAssociatedTokenAddress,
createInitializeMintInstruction,
} = require("@solana/spl-token");
const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey(
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
);
const nftName = "Vnlab(2022): Block chain Q3";
const nftDescription = "This is test nft.";
const nftSymbol = "Q3Nft";
const nftImageUrl = "https://blog.vietnamlab.vn/content/images/1jxWbRXhEbuaZmIFhuPEoBR17O5YbN3_N.png";
async function ipfs_metadata_upload() {
const projectId = '2Eqxxxxx'; // change to PROJECT ID on infura.io
const projectSecret = '5246xxxxxxxxx'; // change to API KEY SECRET on infura.io
const auth =
'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64');
const ipfs = ipfsClient.create({
host: "ipfs.infura.io",
port: 5001,
protocol: "https",
headers: {
authorization: auth,
},
});
const nftMetadata = {
name: nftName,
symbol: nftSymbol,
description: nftDescription,
image: nftImageUrl,
attributes: [
{
trait_type: "size",
value: "very big"
},
{
trait_type: "live in",
value: "land"
},
{
trait_type: "food",
value: "glass"
}
]
};
const nftMetadataIpfs = await ipfs.add(JSON.stringify(nftMetadata));
if (nftMetadataIpfs == null) {
return '';
} else {
return nftMetadataIpfs.path;
}
}
const mintNftSol = catchAsync(async (req, res) => {
/////// UPLOAD METADATA TO IPFS
var metadataUri = await ipfs_metadata_upload();
if (metadataUri == '') {
console.log("IPFS metadata upload failed!");
return;
}
metadataUri = `https://infura-ipfs.io/ipfs/${metadataUri}`;
console.log("IPFS metadata uri: ", metadataUri);
/////// CONNECT SMART-CONTRACT
// Use a provider from value ANCHOR_PROVIDER_URL in .env
const provider = anchor.AnchorProvider.env();
// Configure the client to use the cluster.
anchor.setProvider(provider);
// Read the generated IDL.
const idl = JSON.parse(
require("fs").readFileSync("./src/target/idl/sol_mint_nft.json", "utf8")
);
//Address of the deployed program
const programId = new anchor.web3.PublicKey("94hKFk7nLNyAYTxipCpiHuVt7ADmFBom2vjRFw3bcU7W"); // ProgramId of smart-contract (get from part 1)
//Generate the program client from IDL
const program = new anchor.Program(idl, programId);
console.log("Program Id: ", program.programId.toBase58());
console.log('Mint Size: ', MINT_SIZE);
const lamports = await program.provider.connection.getMinimumBalanceForRentExemption(MINT_SIZE);
console.log("Mint Account Lamports: ", lamports);
const getMetadata = async (mint) => {
return (await anchor.web3.PublicKey.findProgramAddress(
[
Buffer.from("metadata"),
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
mint.toBuffer(),
],
TOKEN_METADATA_PROGRAM_ID
))[0];
};
const mintKey = anchor.web3.Keypair.generate();
const nftTokenAccount = await getAssociatedTokenAddress(
mintKey.publicKey,
provider.wallet.publicKey
);
console.log("NFT Account: ", nftTokenAccount.toBase58());
// create storage account
const mint_tx = new anchor.web3.Transaction().add(
anchor.web3.SystemProgram.createAccount({
fromPubkey: provider.wallet.publicKey,
newAccountPubkey: mintKey.publicKey,
space: MINT_SIZE,
programId: TOKEN_PROGRAM_ID,
lamports,
}),
createInitializeMintInstruction(
mintKey.publicKey,
0,
provider.wallet.publicKey,
provider.wallet.publicKey,
),
createAssociatedTokenAccountInstruction(
provider.wallet.publicKey,
nftTokenAccount,
provider.wallet.publicKey,
mintKey.publicKey
)
);
program.provider.connection._confirmTransactionInitialTimeout = 120000;
const response = await program.provider.sendAndConfirm(mint_tx, [mintKey]);
console.log("Mint key: ", mintKey.publicKey.toString());
console.log("User: ", provider.wallet.publicKey.toString());
const metadataAddress = await getMetadata(mintKey.publicKey);
console.log("Metadata address: ", metadataAddress.toBase58());
const tx = await program.rpc.mintNft(
mintKey.publicKey,
nftName,
nftSymbol,
metadataUri,
{
accounts: {
mintAuthority: provider.wallet.publicKey,
mint: mintKey.publicKey,
tokenAccount: nftTokenAccount,
tokenProgram: TOKEN_PROGRAM_ID,
metadata: metadataAddress,
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
payer: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
}
}
);
console.log("Your transaction signature", tx);
res.send({ 'status' : 'ok', 'account' : program.programId.toBase58() });
});
module.exports = {
mintNftSol,
};
Sau khi lưu lại, chúng ta có thể sử dụng postman để gọi API theo url: http://localhost:3000/v1/mint-nft-sol/create
IPFS metadata uri: https://infura-ipfs.io/ipfs/QmQpXmbXLLc71YsB2n6f22cMmp5eSSs2QimzwVyCsgPTCD
Program Id: 94hKFk7nLNyAYTxipCpiHuVt7ADmFBom2vjRFw3bcU7W
Mint Size: 82
Mint Account Lamports: 1461600
NFT Account: 23PkbNRrAPBuMAhiEEmSSCdHtcz3dVLSjQgCHS1mRKgu
Mint key: 7pQFvu3KmY7Vmai3rjyY16pZt7B1dMtYCg1C7UkFq5FW
User: FGKyx9SUbaxa6XQXLcqQPbq1FmCY9KMBGNUek1isSQEG
Metadata address: EH2vBNJNcG6YSKw8Y2gtMMdWp8U3FWGc6J7GKwJmgkRM
Your transaction signature 3yPcmUf9KePi1yb9j6Leizb4BHYnSDoShBRxbUgdJdTdFG8oJnmR5rhRVcK6otS7EtBdUCiLVy2Lfkzbw9FnoD85
Kiểm tra thông tin NFT vừa được mint trên Solana
Sau khi mint NFT thành công, các bạn có thể kiểm tra thông tin NFT ở đây:
- NFT Account: https://explorer.solana.com/address/23PkbNRrAPBuMAhiEEmSSCdHtcz3dVLSjQgCHS1mRKgu?cluster=devnet
- NFT Mint info: https://explorer.solana.com/address/7pQFvu3KmY7Vmai3rjyY16pZt7B1dMtYCg1C7UkFq5FW?cluster=devnet
Tổng kết
Như vậy mình đã hướng dẫn các bạn xây dựng hệ thống Backend API để mint NFT. Các bạn có thể tham khảo mã nguồn của bài viết này ở đây:
- Backend: https://github.com/gmo-vietnamlab/nft-certificate-backend
- Smart-contract: https://github.com/gmo-vietnamlab/sol-mint-nft
Trong bài viết có sử dụng open source và một số tài liệu tham khảo:
- https://reactjsexample.com/anchor-based-project-to-mint-a-nft-on-solana/
- https://yihau.github.io/solana-web3-demo/
- [Solana Blockchain] Phát triển ứng dụng Blockchain trên Solana Network bằng Anchor Framework (1)
Cảm ơn các bạn đã đọc và theo dõi series bài viết của nhóm block-chain. Rất mong nhận được góp ý của các bạn để mình có thể làm tốt hơn ở những bài viết tiếp theo.