1. Quản trị vòng đời của transaction

1.1. Tại sao phải chú trọng

Điểm yếu nhất của việc tương tác với smart contract là tốc độ gửi giao dịch và chờ kết quả từ node trả về. Đây là đặc điểm gây ảnh hưởng nặng nề đến trải nghiệm (UX) của người dùng. Các giao dịch có thể gặp các vấn đề như là bị lỗi, revert, thiếu gas, v.v... nên khi thiết kế giao diện của người dùng, chúng ta phải chăm chút vào xử lý toàn bộ vòng đời của giao dịch để người dùng biết được rõ nhất trạng thái hiện tại.

1.2. Wagmi hỗ trợ gì?

Các React Hook trong Wagmi có rất nhiều tính năng để giúp bạn quản trị vòng đời của transaction. Ví dụ như:

useReadContract

Hook useReadContract của Wagmi trả về cho chúng ta dữ liệu khi gọi function không thay đổi state. Ngoài ra, hook cũng trả về status object (ví dụ như error, isPending) để chúng ta có thể hiển thị cho người dùng trạng thái giao dịch tương ứng.

// import BaseError and hook useReadContract
import { type BaseError, useReadContract } from 'wagmi'
// import the smart contract's abi file to get the function's interface
import { abi } from './abi'

function ReadContract() {
  const { 
    data: balance, // assign the returned data to the balance variable
    error, // initialize error variable
    isPending // initialize the isPending variable
  } = useReadContract({
    abi, // abi của function
    functionName: 'balanceOf', // function name you want to call
    args: ['0x03A71968491d55603FFe1b11A9e23eF013f75bCF'], // Pass variables to the function
  })
  
  // If isPending is true, the text "Loading..." is displayed, otherwise it disappears
  if (isPending) return <div>Loading...</div> 
  
  // If there is an error, display the div with error message
  if (error) 
    return ( 
      <div>
        Error: {(error as BaseError).shortMessage || error.message} 
      </div> 
    )  

  return (
    // Displays the balance (if any) after converting to string format
    <div>Balance: {balance?.toString()}</div>
  )
}

useWriteContract

Hook useWriteContract của Wagmi trả về cho chúng ta object data có chứa hash của giao dịch sau khi gọi một function thay đổi state của smart contract.

data
`WriteContractReturnType | undefined`

Defaults to `undefined`
The last successfully resolved data for the mutation.

Ngoài ra, hook còn trả về cho chúng ta object isPending để chúng ta có thể sử đụng để hiển thị trạng thái chờ cho giao dịch.

Thêm nữa, Wagmi còn cung cấp thêm useWaitForTransactionReceipt hook để chúng ta chờ kết quả giao dịch với 2 biến trả về là isLoading, isSuccess.

Dưới đây là một ví dụ từ Wagmi v2 docs:

import * as React from 'react' // import react into file
// import BaseError, and 2 hooks useWaitForTransactionReceipt, useWriteContract from wagmi library
import { 
  type BaseError, 
  useWaitForTransactionReceipt, 
  useWriteContract 
} from 'wagmi'
// import the smart contract's abi file to get the function's interface
import { abi } from './abi'
 
export function MintNFT() {
  const { 
    data: hash, // assign the returned data to a variable named hash
    error, // assign error object to error variable
    isPending, // assign the isPending object to the isPending variable
    writeContract // initialize the writeContract function for use
  } = useWriteContract() 

  // function dùng để submit form
  async function submit(e: React.FormEvent<HTMLFormElement>) { 
    e.preventDefault() 
    const formData = new FormData(e.target as HTMLFormElement) 
    const tokenId = formData.get('tokenId') as string 
    writeContract({
      address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // contract address
      abi, // abi of contract
      functionName: 'mint', // function name you want to call
      args: [BigInt(tokenId)], // pass input to function
    })
  } 

  // Call the useWaitForTransactionReceipt hook to initialize the isConfirming and isConfirmed states
  const { isLoading: isConfirming, isSuccess: isConfirmed } = 
    useWaitForTransactionReceipt({ 
      hash, 
    }) 

  // Return format of React
  return (
    <form onSubmit={submit}>
      <input name="address" placeholder="0xA0Cf…251e" required />
      <input name="value" placeholder="0.05" required />
      // If isPending is true, the button will be disabled
      <button 
        disabled={isPending} 
        type="submit"
      >
        {isPending ? 'Confirming...' : 'Mint'} 
        // If isPending is true, display the word "Confirming...", otherwise display the word "Mint"
      </button>
      // If hash is true, the div containing the transaction hash is displayed, otherwise it disappears
      {hash && <div>Transaction Hash: {hash}</div>}
      // If isConfirming is true then display the div with the text "Waiting for confirmation..."
      {isConfirming && <div>Waiting for confirmation...</div>} 
      // If isConfirmed is true, display the div with the text "Transaction confirmed."
      {isConfirmed && <div>Transaction confirmed.</div>}
      // If there is an error, display the div with error message
      {error && ( 
        <div>Error: {(error as BaseError).shortMessage || error.message}</div> 
      )} 
    </form>
  )
}