Работа с контрактами + wagmi.
24 июня 2024 г.
Ниже приведены примеры работы с контрактами с помощью wagmi.
Данные для примера
Для примера, возьмем контракт USDT в сети Binance в testnet.
https://testnet.bscscan.com/token/0x337610d27c682E347C9cD60BD4b3b107C9d34dDd
Адрес контракта токена 0x337610d27c682E347C9cD60BD4b3b107C9d34dDd
Abi контракта можно скачать во вкладке contract. Оно содержит все методы и их аргументы. Abi необходимо при работе с контрактом.
Чтение контракта
Чтение контракта сравнимо с GET запросом. Некоторые методы контракта при чтении требуют аргументы, а некоторые нет.
Чтобы прочитать контракт, можно использовать хук useReadContract, или же метод readContract.
Хук useReadContract
Этот хук используем также, как и любой хук в React, вызывая в компоненте или в собственном хуке.
На данный момент существует проблема при работе с TypeScript. Данные возвращаются с типом unknown. Справится с этой ошибкой мне помогло повторное указание типа.
1import { useAccount, useReadContract } from "wagmi";2import { tokenAbi } from "@/constants/abi/tokenAbi";345export const useTokenDecimals = (6 tokenAddress: `0x${string}`,7) => {8 const { chainId } = useAccount();910 const { data: decimals } = useReadContract({11 abi: tokenAbi,12 address: "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd",13 functionName: "decimals",14 chainId: chainId,15 });1617 const tokenDecimals = decimals as number;1819 return {20 tokenDecimals,21 };22};23
functionName - это метод контракта. В данном примере указан метод decimals. Он возвращает decimals токена - значение, нужное для форматирования сумм.
Здесь пример форматирующей функции, куда передаем decimals.
Метод readContract
На данный момент импортируется из @wagmi/core.
1import {readContract} from "@wagmi/core";
Этот метод можно вызывать в любом месте, в котором вам необходимо.
Например, при отправке транзакции необходимо получить разрешенную сумму. Это метод allowance контракта токена. Он требует аргументы, в примере ниже - это адрес кошелька пользователя и адрес контракта, на который отправляется сумма.
1const allowance = await readContract(config, {2 abi: tokenAbi,3 address: "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd",4 functionName: "allowance",5 chainId: chainId,6 args: [userAddress, contractAddress],7});8
1export const config = defaultWagmiConfig({2 chains,3 projectId,4 metadata,5 ssr: true,6 storage: createStorage({7 storage: cookieStorage8 })9})10
Отправка данных в контракт.
Метод writeContract
Для того, чтобы отправить какие-либо данные в контракт, можно использовать метод writeContract. На данный момент, импортируем его из @wagmi/core. Его можно сравнить с POST ИЛИ PUT запросом.
1import { writeContract } from "@wagmi/core";
В этот метод передаем все те же аргументы, которые передаем и в readContract: abi и адрес контракта, имя функции и ее аргументы, которые можно посмотреть в abi.
Ниже пример: В случае, если allowens у контракта токена меньше суммы которую мы отправляем, нужно сделать дополнительный approve на эту сумму.
1if (amount && +allowanceAmount < amount) {2 const approveTx = await writeContract(config, {3 abi: tokenAbi,4 address: tokenAddress,5 functionName: "approve",6 args: [7 yourContract,8 formatNumberToWEI(amount, tokenDecimals),9 ],10 });11}
config здесь - это config wallet connect.
Здесь в аргументах отправляем адрес контракта, куда отправляются токены и сумму, отформатированную в wei. В константу approveTx вернется hash транзакции.
Документация writeContract.
useWriteContract
Также можно использовать хук useWriteContract. Из него можно вытащить метод writeContract, hash транзакции, статусы и другое.
Документация useWriteContract.
Метод writeContract и статус транзакции
Чтобы получить статус транзакции - узнать, была она успешной или нет, можно воспользоваться методом waitForTransactionReceipt. Его также, на данный момент, импортируем из @wagmi/core.
1import { waitForTransactionReceipt } from "@wagmi/core";
В него передаем hash транзакции, полученный при запуске метода writeContract. Затем уже обрабатываем полученный результат как необходимо.
1const approveTx = await writeContract(config, {2 abi: tokenAbi,3 address: tokenAddress,4 functionName: "approve",5 args: [6 yourContract,7 formatNumberToWEI(amount, tokenDecimals),8 ],9});1011const approveReceipt = await waitForTransactionReceipt(config, {12 hash: approveTx,13});1415if (approveReceipt.status !== `success`) {16 setTransactionLoading(false);17 throw new Error("Approve error");18}
config здесь - это config wallet connect.
Документация waitForTransactionReceipt.
Полный пример кода allowance and approve
1import { useAccount } from "wagmi";2import {3 writeContract,4 waitForTransactionReceipt,5 readContract,6} from "@wagmi/core";7import { useCallback, useState } from "react";89<..>1011const { chainId, address } = useAccount();12const [transactionLoading, setTransactionLoading] =13 useState<boolean>(false);1415<..>1617const approve = useCallback(18 async (19 amount: number,20 tokenAddress: `0x${string}`,21 tokenDecimals: number22 ) => {23 setTransactionLoading(true);24 const allowance = await readContract(config, {25 abi: tokenAbi,26 address: tokenAddress,27 functionName: "allowance",28 chainId: chainId,29 args: [address, yourContract],30 });313233 const allowanceAmount = formatNumberFromWEI(34 allowance as bigint,35 tokenDecimals36 );373839 if (amount && +allowanceAmount < amount) {40 const approveTx = await writeContract(config, {41 abi: tokenAbi,42 address: tokenAddress,43 functionName: "approve",44 args: [45 yourContract,46 formatNumberToWEI(amount, tokenDecimals),47 ],48 });495051 const approveReceipt = await waitForTransactionReceipt(config, {52 hash: approveTx,53 });545556 if (approveReceipt.status !== `success`) {57 setTransactionLoading(false);58 throw new Error("Approve error");59 }606162 return approveReceipt;63 } else {64 return {65 status: "allow",66 };67 }68 },69 [address, chainId, yourContract]70 );71
Отправка транзакции с data
Могут возникнуть случаи, когда для отправки транзакции будет лишь data транзакции - хешированный метод контракта с закодированными аргументами и to - получатель транзакции или адрес вашего контракта.
В этом случае можно использовать метод sendTransaction. На данный момент, импортируем из @wagmi/core
1import { sendTransaction } from '@wagmi/core'23const result = await sendTransaction(config, {4 to: to as `0x${string}`,5 data: data as `0x${string}`,6});
config здесь - это config wallet connect.
В константу result получаем hash транзакции.