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

Bài viết này sẽ cung cấp cho bạn đầy đủ thông tin về các khái niệm cơ bản trong frontend và hướng dẫn bạn cách để xây dựng giao diện tương tác với Solana. Tham khảo ngay!

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

Bài viết này sẽ cung cấp cho bạn đầy đủ thông tin về các khái niệm cơ bản trong frontend và hướng dẫn bạn cách để xây dựng giao diện tương tác với Solana

Solana Accounts

Account trong lập trình Solana được hiểu như là một vùng nhớ để lưu dữ liệu. Chúng là một khối cơ sở trong quá trình phát triển ứng dụng trên Solana.

The Public key

Khóa công khai (thường được viết tắt là pubkey) được gọi là địa chỉ nhận của ví hoặc đơn giản là địa chỉ của nó. Địa chỉ ví có thể được chia sẻ và hiển thị tự do. Khi một bên khác sẽ gửi một số lượng token vào ví, họ cần biết địa chỉ nhận của ví. Tùy thuộc vào việc triển khai của blockchain, địa chỉ cũng có thể được sử dụng để xem thông tin nhất định về ví, chẳng hạn như xem số dư, nhưng không có khả năng thay đổi bất kỳ điều gì về ví hoặc rút bất kỳ thông tin nào.

SOL & Lamports

SOL: Là tên của Solana’s native token, có thể được chuyển đến các node trong một Solana cluster để đổi lấy việc chạy một chương trình trên chuỗi hoặc xác thực đầu ra của nó. Hệ thống có thể thực hiện các khoản thanh toán vi mô của các SOL phân đoạn, được gọi là lamports. Chúng được đặt tên để vinh danh người có ảnh hưởng kỹ thuật lớn nhất của Solana, Leslie Lamport. Một lamport có giá trị là 0,000000001 SOL.

RPC API, connection, commitment

RPC: (Remote Procedure Call) là một tập hợp các giao thức và giao diện mà client tương tác với hệ thống blockchain. Người dùng có thể truy vấn thông tin liên quan đến blockchain (chẳng hạn như số blocks, node, node connection, v.v.) và gửi yêu cầu giao dịch thông qua giao diện RPC.

  • Connection: Kết nối được sử dụng để tương tác với Solana JSON RPC. Bạn có thể sử dụng Kết nối để xác nhận giao dịch, nhận thông tin tài khoản và hơn thế nữa.
  • Commitment: Chỉ số cam kết nhằm cung cấp cho client một thước đo về xác nhận network và mức cổ phần trên một block. Sau đó, client có thể sử dụng thông tin này để đưa ra các biện pháp cam kết của riêng họ.

Web3js

Thư viện Solana-Web3.js nhằm cung cấp toàn bộ thông tin về Solana. Thư viện được xây dựng dựa trên API Solana JSON RPC. Bạn có thể tìm toàn bộ tài liệu cho thư viện @ solana / web3.js tại đây.

Lấy số dư tài khoản bằng web3js

  • Install @solana/web3.js : npm install @solana/web3.js  hoặc yarn add @solana/web3.js
  • Lấy số dư thông qua web3
const getBalanceUsingWeb3 =(address: PublicKey): Promise<number> => {
    const connection = new Connection(clusterApiUrl('devnet'));
    return connection.getBalance(address);
}
  • Address: Địa chỉ ví bạn muốn lấy số dư

Handle invalid address

const addressSubmittedHandler = (address: string) => {
  try {
    const key = new Web3.PublicKey(address)
    } catch (error) {
        alert(error)
    }
  }

Nếu address không hợp lệ thì nó sẽ hiện lên alert: Invalid public key input

Transaction là gì

Bất kỳ sửa đổi nào đối với dữ liệu trên blockchain đều xảy ra thông qua các giao dịch được gửi đến các programs.

  • Transaction instrcutions bao gồm:
  1. Một mã định danh của chương trình
  2. Một array accounts sẽ được đọc hoặc ghi vào
  3. Dữ liệu có cấu trúc như một mảng byte được chỉ định cho chương trình đang được gọi
  • @solana/web3.js cung cấp các chức năng trợ giúp để tạo giao dịch và hướng dẫn. Bạn có thể tạo một giao dịch mới với hàm tạo, new Transaction(). Sau khi được tạo, bạn có thể thêm instruction vào giao dịch bằng phương thức add().

const transaction = new Transaction()

Transaction fees

Phí giao dịch được tích hợp vào nền kinh tế Solana như một khoản bồi thường cho mạng trình xác thực đối với tài nguyên CPU và GPU cần thiết trong quá trình xử lý giao dịch. Không giống như nhiều mạng có thị trường phí, nơi người dùng có thể trả phí cao hơn để tăng cơ hội được đưa vào khối tiếp theo, phí giao dịch Solana mang tính xác định.

Người ký đầu tiên được bao gồm trong loạt người ký trên một giao dịch chịu trách nhiệm thanh toán phí giao dịch. Nếu người ký này không có đủ SOL trong tài khoản của họ để trang trải phí giao dịch, giao dịch sẽ bị loại bỏ.

Sử dụng web3.js để tạo keypair

Import Keypair from @solana/web3.js

const ownerKeypair = Keypair.generate()

Sử dụng web3.js để tạo keypair từ secret key

Nếu bạn đã có một cặp khóa muốn sử dụng, bạn có thể tạo một Cặp khóa từ khóa bí mật bằng cách sử dụng hàm Keypair.fromSecretKey (). Để đảm bảo rằng khóa bí mật của bạn vẫn an toàn, chúng tôi khuyên bạn nên đưa nó vào .env

const secret = JSON.parse(process.env.PRIVATE_KEY ?? "") as number[]
const secretKey = Uint8Array.from(secret)
const keypairFromSecretKey = Keypair.fromSecretKey(secretKey)

Transfer sol với web3js

Tạo một cặp keypair: Nếu chưa có secret key thì sử dụng Keypair.generate() để tạo một key pair mới, nếu đã có secret key thì sử dụng

const initializeKeypair = (): Web3.Keypair => {
 const secret = JSON.parse(process.env.PRIVATE_KEY ?? "") as number[]
 const secretKey = Uint8Array.from(secret)
 const keypairFromSecretKey = Web3.Keypair.fromSecretKey(secretKey)
 return keypairFromSecretKey
}

Sử dụng connection.requestAirdrop để lấy 1sol cho mục đích test

connection.requestAirdrop(payer.publicKey, web3.LAMPORTS_PER_SOL*1)

Hàm transfer sol


const sendSol = async (connection: Web3.Connection, amount: number, to: Web3.PublicKey, sender: Web3.Keypair) => {
 const transaction = new Web3.Transaction()

 const sendSolInstruction = Web3.SystemProgram.transfer(
     {
         fromPubkey: sender.publicKey,
         toPubkey: to,
         lamports: amount,
     }
 )

 transaction.add(sendSolInstruction)

 const sig = await Web3.sendAndConfirmTransaction(connection, transaction, [sender])
 return sig
}

Cuối cùng gọi mọi thứ trong hàm main để thực hiện

const main = async() => {
 const payer = initializeKeypair()
 const connection = new Web3.Connection(Web3.clusterApiUrl('devnet'))
 await connection.requestAirdrop(payer.publicKey, Web3.LAMPORTS_PER_SOL*1)
 const signature = await sendSol(connection, 0.1*Web3.LAMPORTS_PER_SOL, Web3.Keypair.generate().publicKey, payer)
}

Sao chép signature. Mở trình duyệt và truy cập https://explorer.solana.com/?cluster=devnet. Dán signature vào thanh tìm kiếm ở đầu Solana’s Devnet explorer và nhấn enter. Bạn sẽ thấy tất cả các chi tiết về giao dịch. Nếu bạn cuộn xuống dưới cùng, bạn sẽ thấy Nhật ký chương trình, cho biết số lần chương trình đã được ping bao gồm cả ping của bạn.

Wallet

“Ví” dùng lưu trữ khóa bí mật để giữ an toàn. Các tùy chọn lưu trữ an toàn này thường có thể được mô tả dưới dạng ví “phần cứng” (hardware) hoặc “phần mềm”(soft ware). Ví phần cứng là thiết bị lưu trữ tách biệt với máy tính của bạn. Ví phần mềm là ứng dụng bạn có thể cài đặt trên các thiết bị hiện có của mình.

  • Ví phần mềm (Software wallets) thường có dạng một phần mở rộng của trình duyệt (browser extension). Điều này giúp các trang web có thể tương tác dễ dàng với ví. Những tương tác như vậy thường được giới hạn ở:
  • Xem khóa công khai (địa chỉ) của ví
  • Gửi giao dịch để người dùng phê duyệt
  • Gửi một giao dịch được chấp thuận đến network
  • Khi một giao dịch được gửi đi, người dùng cuối có thể “xác nhận” giao dịch đó và gửi nó đến mạng với “chữ ký” của họ.
  • Việc ký kết các giao dịch yêu cầu sử dụng khóa bí mật (secret key) của bạn. Bằng cách cho phép một trang web gửi giao dịch đến ví của bạn và để ví xử lý việc ký, bạn đảm bảo rằng bạn không bao giờ để lộ khóa bí mật của mình trên trang web. Thay vào đó, bạn chỉ chia sẻ khóa bí mật với ứng dụng ví.

Phantom Wallet

Một trong những ví phần mềm được sử dụng rộng rãi nhất trong hệ sinh thái Solana là Phantom. Phantom hỗ trợ một số trình duyệt phổ biến nhất và có ứng dụng di động để kết nối khi di chuyển.

Solana’s wallet-adapter

  • Solana’s Wallet-Adapter là một bộ thư viện mà bạn có thể sử dụng để đơn giản hóa quá trình hỗ wallet browser extensions.
  • Bộ điều hợp Wallet của Solana bao gồm nhiều gói mô-đun. Chức năng cốt lõi được tìm thấy trong @ solana / wallet-adapter-base và @ solana / wallet-adapter-react.
  • Ngoài ra còn có các gói cung cấp các thành phần cho các khung giao diện người dùng chung.
  • Cuối cùng, có các gói là bộ điều hợp (adapter)  cho các ví cụ thể, bao gồm cả Phantom. Bạn có thể sử dụng @ solana / wallet-adapter-wallet để bao gồm tất cả các ví được hỗ trợ hoặc bạn có thể chọn một gói ví cụ thể như @ solana / wallet-adapter-phantom.

Transaction & Instruction

a. Transaction:

  • Giao dịch là cách chúng ta gửi thông tin đến blockchain để được xử lý
  • Một transaction bao gồm:
  • Một danh sách accounts mà nó dự định đọc hoặc ghi vào
  • Một hoặc nhiều instructions
  • Một blockhash gần đây (recent blockhash)
  • Một hoặc nhiều signatures

b. Instruction

- Một instruction bao gồm:

  • Program ID (public key) của chương trình dự định
  • Một mảng liệt kê mọi tài khoản sẽ được đọc từ hoặc ghi vào trong quá trình thực thi
  • Một byte buffer của instruction data
  • Việc xác định chương trình bằng khóa công khai (public key) của nó đảm bảo rằng hướng dẫn được thực hiện bởi chương trình chính xác.
  • Byte buffer cho phép bạn chuyển dữ liệu bên ngoài vào một chương trình.
  • Bạn có thể thêm nhiều instruction trong một transaction. Thời gian chạy Solana sẽ xử lý các hướng dẫn này theo thứ tự và nguyên tử. Nói cách khác, nếu mọi lệnh thành công thì toàn bộ giao dịch sẽ thành công, nhưng nếu một lệnh duy nhất không thành công thì toàn bộ giao dịch sẽ thất bại ngay lập tức mà không có tác dụng phụ.

Programs

  • Mọi thứ trong Solana đều là tài khoản. Chương trình là tài khoản lưu trữ code và được đánh dấu là có thể thực thi được. Code có thể được thực thi bởi Solana runtime khi được hướng dẫn làm như vậy.
  • Tuy nhiên, bản thân các chương trình là không trạng thái (stateless). Họ không thể sửa đổi dữ liệu trong tài khoản của họ. Chúng chỉ có thể duy trì trạng thái bằng cách lưu trữ dữ liệu trong các tài khoản khác có thể được tham chiếu tại một số thời điểm khác. Việc hiểu cách sử dụng các tài khoản này và cách tìm chúng là rất quan trọng đối quá trình phát triển ứng dụng Solana.

getMultipleAccounts

  • Lấy tất cả accounts mà không có dữ liệu

const accountsWithoutData = await connection.getProgramAccounts(
	programId,
	{
		dataSlice: { offset: 0, length: 0 }
	}
)
const accountKeys = accountsWithoutData.map(account => account.pubkey)

  • Với danh sách accounts vừa lấy được bạn có thể lấy toàn bộ data với getMultipleAccountsInfo
const accountInfos = await connection.getMultipleAccountsInfo(accountKeys)
const deserializedObjects = accountInfos.map((accountInfo) => {
	// put logic to deserialize accountInfo.data here
})

Serialize

  • Ngoài việc biết thông tin nào cần instructions data buffer, bạn cũng cần phải tuần tự hóa nó đúng cách. Bộ nối tiếp phổ biến nhất được sử dụng ở Solana là Borsh.
  • Borsh là 1 thư viện js giúp xử lý tuần tự hoá dữ liệu thông thường thành buffer. Ngoài ra còn có các gói khác được xây dựng trên borsh cố gắng làm cho quá trình này trở nên dễ dàng hơn. Chúng tôi sẽ sử dụng thư viện @ project-serum / borsh có thể được cài đặt bằng npm.
  • Sau khi buffer data được tạo đúng cách và dữ liệu được tuần tự hóa, tất cả những gì còn lại là xây dựng giao dịch. Điều này tương tự như những gì bạn đã làm khi tạo intrucstion. Xem ví dụ dưới đây:
import * as borsh from '@project-serum/borsh'
import * as web3 from '@solana/web3.js'

const equipPlayerSchema = borsh.struct([
	borsh.u8('variant'),
	borsh.u16('playerId'),
	borsh.u256('itemId')
])

const buffer = Buffer.alloc(1000)
equipPlayerSchema.encode({ variant: 2, playerId: 1435, itemId: 737498 }, buffer)

const instructionBuffer = buffer.slice(0, equipPlayerSchema.getSpan(buffer))

const endpoint = web3.clusterApiUrl('devnet')
const connection = new web3.Connection(endpoint)

const transaction = new web3.Transaction()
const instruction = new web3.TransactionInstruction({
	keys: [
		{
      pubkey: player.publicKey,
      isSigner: true,
      isWritable: false,
    },
    {
      pubkey: playerInfoAccount,
      isSigner: false,
      isWritable: true,
    },
    {
      pubkey: web3.SystemProgram.programId,
      isSigner: false,
      isWritable: false,
    }
	],
	data: instructionBuffer,
	programId: PROGRAM_ID
})

transaction.add(instruction)

web3.sendAndConfirmTransaction(connection, transaction, [player]).then((txid) => {
	console.log(`Transaction submitted: https://explorer.solana.com/tx/${txid}?cluster=devnet`)
})
  • player, playerInfoAccount, and PROGRAM_ID: Đã được định nghĩa ở đâu đó
  • player: là user’s public key
  • playInfoAccount: là public key của các account nơi các thay đổi về dữ liệu được ghi
  • SystemProgram: sẽ được sử dụng trong quá trình thực hiện lệnh.

Deserializing

  • Dữ liệu của AccountInfo là kiểu buffer. Để sử dụng được bạn cần phải giải mã nó thành dữ liệu thông thường. Điều này tương tự như quá trình tuần tự hoá mà chúng ta đã đề cập phía trên. Chúng ta cũng sẽ sử dụng Borsch và @ project-serum / borsh để làm điều đó.
  • Việc deserializing account yêu cầu bạn phải biết trước về bố cục account. Khi tạo các chương trình của riêng bạn, bạn sẽ xác định cách thực hiện điều này như một phần của quá trình đó. Nhiều chương trình cũng có tài liệu về cách giải mã dữ liệu account.Ngược lại, nếu mã chương trình có sẵn, bạn có thể xem nguồn và xác định cấu trúc theo cách đó. Xem ví dụ dưới đây:
import * as borsh from "@project-serum/borsh";

borshAccountSchema = borsh.struct([
	borsh.bool('initialized'),
	borsh.u16('playerId'),
	borsh.str('name')
])
  • Khi bạn đã xác định bố cục của mình, chỉ cần gọi .decode (buffer) trên lược đồ.
import * as borsh from "@project-serum/borsh";

borshAccountSchema = borsh.struct([
	borsh.bool('initialized'),
	borsh.u16('playerId'),
	borsh.str('name')
])

const { playerId, name } = borshAccountSchema.decode(buffer)

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

Workshop 1: Introduction to 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.