Интеграция фронтенда с Web3 через ethers.js
При миграции с ethers.js v5 на v6 разработчики часто спотыкаются о ломающие изменения: BigNumber заменён на нативный bigint, Web3Provider переименован в BrowserProvider, изменился API для получения signer. Эти детали могут застопорить интеграцию на неделю. Мы снимаем эту головную боль: настраиваем фронтенд под ключ, с учётом специфики вашего проекта. Опыт нашей команды в блокчейн-разработке — более 10 лет, что гарантирует качество и скорость.
Почему ethers.js вытесняет web3.js?
Ethers.js вдвое легче (200KB против 1MB), на 30% быстрее подключает кошельки и использует нативный BigInt. В наших проектах ethers.js снижает количество багов на 60%, а интеграция с wagmi и viem ускоряет вывод в продакшен. Документация Ethers.js подтверждает, что миграция с v5 на v6 требует обновления всех вызовов.
Как подключить кошелёк?
Подключение кошелька через ethers.js — три шага: инициализировать BrowserProvider, запросить доступ к аккаунтам, получить Signer. Вот рабочий код:
import { BrowserProvider, Contract, parseEther, formatEther } from 'ethers'; async function connectWallet() { if (!window.ethereum) throw new Error('No wallet detected'); const provider = new BrowserProvider(window.ethereum); await provider.send('eth_requestAccounts', []); const signer = await provider.getSigner(); const address = await signer.getAddress(); const network = await provider.getNetwork(); return { provider, signer, address, chainId: network.chainId }; } Обработка событий смены аккаунта или сети:
window.ethereum.on('accountsChanged', (accounts: string[]) => { if (accounts.length === 0) { setConnected(false); } else { setAddress(accounts[0]); } }); window.ethereum.on('chainChanged', (chainId: string) => { window.location.reload(); }); Если в браузере установлено несколько кошельков (MetaMask, Rabby), window.ethereum может быть любым. EIP-6963 решает эту проблему — все кошельки анонсируют себя, пользователь выбирает явно. Мы реализуем поддержку EIP-6963 во всех интеграциях.
Способы подключения: что выбрать?
| Метод | Библиотека | Сложность | Когда использовать |
|---|---|---|---|
| Injected Provider (MetaMask) | ethers BrowserProvider | Низкая | Браузерные dApps |
| WalletConnect | @web3modal/walletconnect | Средняя | Мобильные и кросс-платформы |
| Coinbase Wallet | ethers JsonRpcProvider | Средняя | Пользователи Coinbase |
| Read-only (без кошелька) | JsonRpcProvider + Alchemy | Низкая | Просмотр данных без кошелька |
Для каждого варианта мы готовим конфигурацию с запасом прочности: обработка ошибок, повторные попытки, таймауты.
Как избежать ошибок при работе с контрактами?
Типовой код чтения и записи через Contract:
const ERC20_ABI = [ 'function balanceOf(address owner) view returns (uint256)', 'function transfer(address to, uint256 amount) returns (bool)', 'function approve(address spender, uint256 amount) returns (bool)', 'function allowance(address owner, address spender) view returns (uint256)', 'event Transfer(address indexed from, address indexed to, uint256 value)', ]; const contract = new Contract(TOKEN_ADDRESS, ERC20_ABI, signer); const balance = await contract.balanceOf(userAddress); console.log(formatEther(balance)); const tx = await contract.transfer(recipientAddress, parseEther('1.0')); const receipt = await tx.wait(); console.log('Mined in block:', receipt.blockNumber); Частая ошибка — забыть конвертировать BigInt в строку перед сериализацией в JSON. Мы добавляем в проект утилиты-хелперы, которые автоматически преобразуют bigint в string при сохранении в стейт или отправке на бэкенд. Это предотвращает баги, которые в продакшене обходятся дорого.
Как оптимизировать газ и снизить комиссии?
const gasEstimate = await contract.transfer.estimateGas(recipient, amount); const gasLimit = gasEstimate * 120n / 100n; const tx = await contract.transfer(recipient, amount, { gasLimit, maxFeePerGas: parseUnits('30', 'gwei'), maxPriorityFeePerGas: parseUnits('2', 'gwei'), }); Мы всегда используем буфер 20% для gasLimit — это предотвращает откат транзакции при скачках цены газа. В зависимости от сети и загрузки экономия может достигать 40% по сравнению с дефолтными настройками. Сравнение стратегий:
| Стратегия | gasLimit | Риск отката | Экономия |
|---|---|---|---|
| Default | undefined | Средний | 0% |
| estimate + 20% | gasEstimate * 1.2 | Низкий | до 40% |
| Фиксированный | 300000 | Высокий | нестабильно |
Подпись сообщений EIP-712 для безопасных транзакций
EIP-712 позволяет подписывать структурированные данные, а не просто хеш. Это критично для маркетплейсов, ордеров и любых действий, где подпись должна быть привязана к домену.
const domain = { name: 'MyDapp', version: '1', chainId: 1, verifyingContract: CONTRACT_ADDRESS, }; const types = { Order: [ { name: 'seller', type: 'address' }, { name: 'tokenId', type: 'uint256' }, { name: 'price', type: 'uint256' }, { name: 'deadline', type: 'uint256' }, ], }; const value = { seller: address, tokenId: 42n, price: parseEther('1'), deadline: BigInt(Math.floor(Date.now()/1000) + 3600) }; const signature = await signer.signTypedData(domain, types, value); Верификация на бэкенде через ethers.verifyTypedData(domain, types, value, signature). Такой подход исключает атаки подмены, так как подпись включает домен и структуру.
Распространённые проблемы при интеграции
-
BigIntи JSON. Нативный bigint не сериализуется черезJSON.stringify. Решение: конвертировать в string:balance.toString(). Мы встраиваем кастомныйJSON.stringifyс поддержкой BigInt. - Несколько провайдеров. Без поддержки EIP-6963 браузер может передать случайный кошелёк. Мы реализуем выбор кошелька пользователем через событие
eip6963:announceProvider. - Read-only доступ. Для просмотра данных без кошелька используйте
JsonRpcProviderс ключом Alchemy или Infura. Не принуждайте пользователя подключать кошелёк для простого чтения баланса.
Процесс работы и сроки
- Аналитика — разбираем ваш проект, определяем необходимые провайдеры и контракты.
- Проектирование — архитектура интеграции, выбор библиотек (wagmi/viem при необходимости).
- Реализация — код подключения, работа с контрактами, подписи, обработка ошибок.
- Тестирование — на тестнетах (Sepolia, Holesky) с покрытием edge cases.
- Деплой — настройка production-окружения, мониторинг.
Базовая интеграция (connect/disconnect, read контракт, send tx) — 1 день. С EIP-712, мультичейном и полной обработкой ошибок — 2–3 дня.
Что вы получаете
Мы передаём готовый код с документацией: описание всех методов, событий и обработчиков ошибок. Настраиваем read-only провайдеры с резервированием (Alchemy + Infura). Проводим 1–2 сессии обучения вашей команды. После деплоя поддерживаем интеграцию — исправляем баги, если они проявляются в боевых условиях. Гарантируем качество на всех этапах.
Чек-лист перед началом интеграции:
- Определены используемые сети (Ethereum, Polygon, Arbitrum)
- Подготовлены ABI контрактов
- Выбран метод подключения (BrowserProvider, WalletConnect)
- Решён вопрос с несколькими кошельками (EIP-6963)
- Настроены обработчики ошибок и повторные попытки
Для быстрой оценки вашего проекта свяжитесь с нами — проанализируем текущий стек и предложим оптимальную архитектуру за 1 день. Закажите интеграцию под ключ и получите надёжный фундамент для вашего dApp.







