Интеграция кошелька с MetaMask

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Интеграция кошелька с MetaMask
Простая
~2-3 рабочих дня
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Интеграция кошелька с MetaMask

MetaMask инжектирует window.ethereum в страницу — и на этом прямая работа с провайдером обычно заканчивается. В 2024 году писать window.ethereum.request({ method: 'eth_requestAccounts' }) напрямую — это либо прототип, либо технический долг. Правильная интеграция через wagmi + viem обрабатывает все edge cases: несколько кошельков в браузере, смена сети, разрыв соединения, мобильный MetaMask через deeplink.

Стек и подключение

wagmi v2 + viem — стандарт для современных dApp. Wagmi управляет состоянием подключения, автоматическим переподключением, событиями. Viem — типизированный low-level клиент вместо ethers.js.

import { createConfig, http } from 'wagmi'
import { mainnet, base } from 'wagmi/chains'
import { metaMask } from 'wagmi/connectors'

export const config = createConfig({
  chains: [mainnet, base],
  connectors: [metaMask()],
  transports: {
    [mainnet.id]: http(),
    [base.id]: http(),
  },
})

Кнопка подключения:

import { useConnect, useAccount, useDisconnect } from 'wagmi'

function ConnectButton() {
  const { address, isConnected } = useAccount()
  const { connect, connectors } = useConnect()
  const { disconnect } = useDisconnect()

  if (isConnected) {
    return (
      <button onClick={() => disconnect()}>
        {address?.slice(0, 6)}...{address?.slice(-4)}
      </button>
    )
  }

  const metamask = connectors.find(c => c.id === 'metaMaskSDK')
  return (
    <button onClick={() => connect({ connector: metamask! })}>
      Connect MetaMask
    </button>
  )
}

Смена сети и добавление кастомной сети

Частая проблема: пользователь на Ethereum mainnet, dApp работает на Base. Нужно запросить переключение, а если сеть не добавлена — добавить её.

import { useSwitchChain } from 'wagmi'

function NetworkGuard({ children, requiredChainId }: Props) {
  const { chainId } = useAccount()
  const { switchChain, isPending } = useSwitchChain()

  if (chainId !== requiredChainId) {
    return (
      <button
        onClick={() => switchChain({ chainId: requiredChainId })}
        disabled={isPending}
      >
        Switch to Base
      </button>
    )
  }
  return children
}

wagmi автоматически вызывает wallet_addEthereumChain если wallet_switchEthereumChain возвращает ошибку 4902 (chain not found).

Подпись сообщений и транзакции

EIP-712 typed data signing — для структурированных подписей (permit, orders, auth tokens):

import { useSignTypedData } from 'wagmi'

const { signTypedData } = useSignTypedData()

signTypedData({
  domain: { name: 'MyApp', version: '1', chainId: 8453 },
  types: {
    Login: [
      { name: 'address', type: 'address' },
      { name: 'nonce', type: 'string' },
    ],
  },
  primaryType: 'Login',
  message: { address: userAddress, nonce: sessionNonce },
})

Для SIWE (Sign-In with Ethereum) — siwe библиотека генерирует стандартизированное сообщение, backend верифицирует через SiweMessage.verify().

Обработка ошибок

MetaMask возвращает специфичные error codes:

  • 4001 — пользователь отклонил запрос
  • 4902 — сеть не найдена
  • -32002 — запрос уже ожидает подтверждения (второй вызов connect пока первый висит)
  • -32603 — internal JSON-RPC error (чаще всего insufficient gas или revert)
const { error } = useConnect()

useEffect(() => {
  if (error?.name === 'UserRejectedRequestError') {
    toast('Подключение отменено')
  } else if (error?.name === 'ConnectorAlreadyConnectedError') {
    // уже подключён, игнорируем
  }
}, [error])

Mobile deeplink

На мобильных MetaMask открывается через deeplink metamask://dapp/<your-url>. wagmi MetaMask connector автоматически обрабатывает это через MetaMask SDK. Для корректной работы на iOS нужен HTTPS (не HTTP) и правильно настроенный WagmiProvider на верхнем уровне приложения.