javascript

Web3 Frontend — с чего начать?

  • воскресенье, 27 июля 2025 г. в 00:00:02
https://habr.com/ru/articles/931352/

Создание фронтенда для Web3-приложений - это не только дизайн, кнопки и React. Это мост между пользователем и блокчейном. И ты, как фронтенд-разработчик - тот, кто этот мост строит.

В этой статье ты узнаешь:

  • Что такое Web3 Frontend и чем он отличается от Web2

  • Какие инструменты тебе реально нужны

  • Как начать с нуля

  • Как использовать wagmi - главный инструмент Web3-интерфейсов

Что такое Web3 Frontend?

Web3 Frontend — это интерфейс для децентрализованного приложения (dApp), который работает вместо сервера, напрямую общаясь с блокчейном и кошельком пользователя.

Если ты еще не знаешь что такое децентрализация, то обязательно прочитай об этом отдельно. Это целая концепция вокруг которой все web3 и построено.

Ты не отправляешь запросы на backend, как в Web2. Ты взаимодействуешь с сетью (Ethereum, Polygon, Base и т.п.) через смарт-контракты и подключённый кошелёк.

Что тебе нужно для старта?

1. Библиотеки Web3

Библиотека

Назначение

wagmi

Основной инструмент для Web3-интерфейса

viem

RPC-клиент для вызова контрактов (вместо ethers.js)

@rainbow-me/rainbowkit

Подключение кошельков с UI

ethers.js (опц.)

Старый, но популярный Web3-клиент

2. Базовые понятия Web3 - кратко и по делу

Чтобы комфортно работать с wagmi и любыми Web3-инструментами, нужно понимать, как устроен фундамент. Ниже представлены все ключевые термины, которые ты будешь встречать в dApp’ах снова и снова.

address → адрес кошелька или контракта

Это строка, которая представляет пользователя или контракт в сети. Она всегда начинается с 0x, содержит 40 символов.

Пример:
0x1234567890abcdef1234567890abcdef12345678

Есть два вида:

  • EOA (Externally Owned Account) — обычный кошелёк (например, MetaMask)

  • Contract Account — адрес, по которому развёрнут смарт-контракт

Чтобы отображать адрес красиво, используют ENS (например: vitalik.eth)

token → цифровой актив на балансе

ERC-20 — взаимозаменяемые (USDC, DAI):
поддерживают balanceOf, transfer, approve, decimals
Используй useBalance({ token }) в wagmi для получения баланса

ERC-721 — NFT (уникальные ID):
функции ownerOf, tokenURI, transferFrom
Через tokenURI получаешь метаданные с image, name

decimals — количество знаков после запятой:
ETH = 18, USDC = 6
Используй parseUnits / formatUnits из viem для конвертации

approve + allowance — разрешение контракту тратить токены
Обязательно для свапов, стейкинга и NFT-маркетплейсов

chainId → идентификатор сети

Показывает, к какой сети подключён пользователь. Каждый блокчейн имеет уникальный chainId.

Примеры:

  • Ethereum Mainnet → 1

  • Goerli Testnet → 5

  • Polygon → 137

  • Arbitrum → 42161

  • Optimism → 10

Ты используешь chainId, чтобы:

  • определить активную сеть

  • переключать сеть (через switchNetwork)

  • отображать правильные токены/цены

provider → канал связи с блокчейном

Это объект, через который ты читаешь данные из сети: балансы, блоки, события. Он не может подписывать транзакции.

→ В wagmi и viem провайдер создаётся через publicProvider() или RPC-провайдеры вроде Alchemy, Infura и т. д.

→ Примеры возможностей:

  • Получить текущий блок

  • Прочитать состояние контракта

  • Подписаться на события

signer → тот, кто подписывает транзакции

Это расширение provider с доступом к приватному ключу (который держит MetaMask).

С помощью signer ты можешь:

  • отправить транзакцию (sendTransaction)

  • подписать сообщение (signMessage)

  • вызывать contract.write() (например, mint())

→ В wagmi тебе не нужно напрямую работать с signer, он используется внутри хуков типа useContractWrite.

smart contract, ABI, read/write

Смарт-контракт — это программа на блокчейне.
ABI (Application Binary Interface) — это инструкция для твоего фронтенда: какие функции у контракта, какие параметры, что возвращается.

Пример ABI-функции:

{
  "name": "mint",
  "type": "function",
  "stateMutability": "payable",
  "inputs": [],
  "outputs": []
}

Есть два типа вызовов:

  • read → бесплатный, безопасный, не требует подписи (например, totalSupply(), balanceOf())

  • write → платный (gas), требует подписи (например, mint(), transfer())

gas, fee, nonce

gas → топливо, которое тратится на выполнение кода
gasPrice → цена за одну единицу газа (в Gwei)
fee = gas × gasPrice (тебе нужно заплатить в ETH или другой валюте)
nonce → счётчик количества транзакций от адреса (чтобы они шли по порядку)

Все write-функции тратят gas, даже если они не срабатывают (например, revert’ятся)

network, chain, L2

network = chain = блокчейн-среда, в которой работает dApp
L1 → основной уровень (Ethereum Mainnet)
L2 → надстройка над L1 для ускорения и удешевления транзакций

Ты должен всегда:

  • проверять, в какой сети находится пользователь

  • уметь предлагать переключение сети (например, с mainnet на L2)

  • работать с несколькими chainId через wagmi-конфигурацию

tokens, decimals, balance

На блокчейне балансы измеряются в целых числах, без запятой
- ETH имеет 18 знаков после запятой
- USDC — 6

В wagmi ты используешь useBalance({ address }) — и получаешь уже отформатированное значение

Краткая таблица для повторения

Термин

Пояснение

address

Адрес пользователя или контракта (0x...)

chainId

ID блокчейна

provider

Канал к блокчейну (чтение)

signer

Подписант транзакций (MetaMask и т.д.)

contract

Программа на блокчейне

ABI

Описание функций контракта

gas

Стоимость исполнения

read

Чтение без газа

write

Транзакции с подписью и оплатой

nonce

Счётчик транзакций

signMessage

Подпись строки через кошелёк

SIWE

Авторизация без пароля через Ethereum

token

цифровой актив

wagmi — must-have для Web3 Frontend

Библиотека wagmi — это набор React-хуков, которые позволяют:

  • Подключать кошелёк

  • читать/записывать в смарт-контракт

  • Получать баланс, ENS, chain info

  • Подписывать сообщения и транзакции

🧪 Пример: минимальный dApp с wagmi

1. Установка:

npm install wagmi viem @rainbow-me/rainbowkit

2. Настройка WagmiConfig

// main.tsx
import { WagmiConfig, createConfig, configureChains } from 'wagmi'
import { publicProvider } from 'wagmi/providers/public'
import { mainnet, polygon, optimism } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const { publicClient, webSocketPublicClient } = configureChains(
  [mainnet, polygon, optimism],
  [publicProvider()]
)

const config = createConfig({
  autoConnect: true,
  publicClient,
  webSocketPublicClient,
})

const queryClient = new QueryClient()

export function Web3Provider({ children }) {
  return (
    <WagmiConfig config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiConfig>
  )
}

3. Подключение кошелька

// ConnectWallet.tsx
import { useAccount, useConnect, useDisconnect } from 'wagmi'
import { InjectedConnector } from 'wagmi/connectors/injected'

export function ConnectWallet() {
  const { connect } = useConnect({
    connector: new InjectedConnector(),
  })
  const { disconnect } = useDisconnect()
  const { isConnected, address } = useAccount()

  return isConnected ? (
    <div>
      <p>Вы подключены: {address}</p>
      <button onClick={() => disconnect()}>Отключиться</button>
    </div>
  ) : (
    <button onClick={() => connect()}>Подключить MetaMask</button>
  )
}

4. Чтение баланса

// Balance.tsx
import { useBalance, useAccount } from 'wagmi'

export function Balance() {
  const { address } = useAccount()
  const { data, isLoading } = useBalance({ address })

  if (isLoading) return <p>Загрузка...</p>

  return <p>Баланс: {data?.formatted} {data?.symbol}</p>
}

5. Вызов функции смарт-контракта

// MintNFT.tsx
import { usePrepareContractWrite, useContractWrite } from 'wagmi'
import { parseEther } from 'viem'

const contractAddress = '0x123...' // твой контракт
const abi = [
  {
    name: 'mint',
    type: 'function',
    stateMutability: 'payable',
    inputs: [],
    outputs: [],
  },
]

export function MintNFT() {
  const { config } = usePrepareContractWrite({
    address: contractAddress,
    abi,
    functionName: 'mint',
    value: parseEther('0.01'),
  })

  const { write, isLoading, isSuccess } = useContractWrite(config)

  return (
    <button disabled={!write || isLoading} onClick={() => write?.()}>
      {isLoading ? 'Минтим...' : 'Минт NFT'}
      {isSuccess && <p>Успех!</p>}
    </button>
  )
}

Что по итогу?

Web3 Frontend - это не сложно, если начать с правильных инструментов.
Твоя цель - не перепридумать backend, а создать безопасный, быстрый и дружелюбный интерфейс поверх блокчейна. И в этом тебе максимально помогает wagmi.

В следующих частях мы остановимся более подробно на живых примерах dapp приложений и разберем как это все работает под капотом.