Перейти к содержанию

Подключение Web3-кошелька по кнопке

Оглавление курса «Web3-интерфейс: реестр в браузере» · Урок 11 из 30.

Обнаруженный провайдер ещё не означает подключённый аккаунт. Пользователь должен самостоятельно разрешить сайту получить адрес. В этом уроке реализуем кнопку подключения, обработаем отказ и проверим выбранную сеть. Результат — интерфейс получает публичный контекст для подготовки записи, сохраняя чтение доступным без кошелька.

Используем выбранный объект EIP-1193 из предыдущего урока. Аккаунт издателя известен из реестра, но не передаётся в запрос как приказ кошельку. Выбор аккаунта остаётся за пользователем. Код подготовлен для будущей локальной лаборатории 31337; запрос разрешения, кошелёк и подпись в данной работе не запускались.

Разрешение по кнопке

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

Сеть проверяется отдельно через eth_chainId. Значение возвращается шестнадцатеричной строкой. Для сравнения с небольшим учебным значением можно использовать BigInt, не теряя смысл формата. Это не та же операция, что преобразование баланса: идентификатор сети не имеет единицы ETH и не форматируется как денежная сумма.

Вызов разрешения может завершиться после того, как пользователь изменил сеть. Поэтому читаем сетевой контекст после получения аккаунтов, а события изменений позже будут инвалидировать состояние. Однократная проверка перед показом окна не удерживает внешний кошелёк в неизменном состоянии.

Самостоятельный модуль подключения

Создадим connect-wallet.js. В этом шаге модуль возвращает адрес и цепочку; Wallet Client для записи появится отдельно. Имя аккаунта остаётся публичным адресом, а не приватным ключом.

import { getAddress, isAddress } from 'viem';

export async function connectWallet(provider) {
  const accounts = await provider.request({ method: 'eth_requestAccounts' });
  if (!Array.isArray(accounts) || !isAddress(accounts[0])) {
    throw new Error('Аккаунт не выбран');
  }
  const chainHex = await provider.request({ method: 'eth_chainId' });
  const chainId = BigInt(chainHex);
  if (chainId !== 31337n) throw new Error('Выберите локальную сеть 31337');
  return {
    provider,
    account: getAddress(accounts[0]),
    chainId: 31337,
  };
}

Массив разрешённых аккаунтов может быть пустым. Нельзя продолжать подготовку запроса с undefined и позволять библиотеке выбрать неявного отправителя. Полученный первый адрес отражает принятую нами политику простого интерфейса. Если продукт позволяет несколько аккаунтов, выбор отображается явно и входит в контекст каждого действия.

getAddress нормализует представление уже проверенного адреса. Оно не удостоверяет личность человека и не проверяет права публикации. Для издательской формы сравниваем адрес с publisher, прочитанным из выбранного реестра. Даже если совпадение найдено, право окончательно проверит контракт при исполнении транзакции.

Отказ не является аварией

Пользователь может закрыть окно или отказать в разрешении. Стандарт EIP-1193 предусматривает ошибку отказа с кодом 4001. Интерфейс сообщает «Подключение отменено» и возвращает кнопку в доступное состояние. Публичная карточка остаётся на месте. Не следует красить весь сайт как сломанный или автоматически открывать окно повторно.

Ошибки авторизации, неподдерживаемого метода и отключённого провайдера имеют другой смысл. Для пользователя полезны короткие сообщения, а для диагностики сохраняется категория и безопасная техническая причина. Произвольный текст исключения расширения не нужно вставлять как HTML. Название метода также не заменяет понятное объяснение нужного действия.

Если сеть отличается, приложение не объявляет аккаунт неподходящим вообще: он может стать пригодным после явного переключения окружения. Различайте «не выбран издатель» и «выбрана другая сеть». Эти ситуации требуют разных исправлений, а одинаковая надпись «кошелёк неверный» лишает посетителя полезной информации.

Повторное подключение

После успешного разрешения можно получать доступные адреса через eth_accounts без нового окна. Однако наличие ранее выданного разрешения не означает, что выбранный аккаунт или сеть не изменились. Восстановление страницы проверяет свежий контекст и не включает кнопку записи только на основании сохранённой строки в localStorage.

Сохранение выбора расширения также не сохраняет объект провайдера между загрузками. После новой загрузки проводится обнаружение, пользовательский выбор сопоставляется с доступными вариантами, а состояние разрешения читается заново. Приложение не может восстановить JavaScript-объект из JSON как действующую способность подписывать.

Во время подключения пользователь может перейти в другой раздел сайта. Поздний ответ разрешения не должен неожиданно открыть форму на новом экране. Используем поколение состояния и очистку, как в контроллере чтения. Операции кошелька остаются внешними действиями, но результат применяется только к актуальному экрану.

Адрес и приватность

Адрес публичен в цепочке, но связь адреса с посещением конкретного сайта создаёт дополнительные сведения. Поэтому запрашиваем его только для нужного действия и не отправляем в аналитику без соответствующей задачи. Чтение курса 17 не требует такой связи. Запросить аккаунт «на всякий случай» — лишняя зависимость и лишнее раскрытие данных.

На странице показываем достаточно символов для узнавания аккаунта и предоставляем доступ к полному адресу. Приватный ключ, seed-фраза и пароль кошелька не являются входными данными формы. Сайт не должен просить их даже для локальной лаборатории. Подпись выполняется внутри выбранного кошелька отдельным согласованным действием.

Ожидаемая последовательность

Откройте мысленно страницу с доступной публичной карточкой. После нажатия кнопки состояние подключения становится ожиданием. При согласии появляется нормализованный адрес и сеть 31337. При отказе адрес остаётся пустым. При выборе другой сети форма записи недоступна до нового актуального контекста. Во всех случаях чтение остаётся независимым.

Для упражнения сформулируйте сообщения для пустого массива адресов, ошибки 4001 и сети с другим идентификатором. Затем определите, какие поля формы допустимо сохранить при каждом случае. Ожидаемый итог — подключение не считается ни входом в личный кабинет, ни публикацией курса. Последующие уроки отдельно рассмотрят полномочия и подпись. Семантика провайдера приведена в EIP-1193.

Одно ожидание разрешения

Контроллер подключения хранит флаг активного запроса, а не полагается только на отключённый DOM. Два быстрых нажатия могут попасть в очередь раньше перерисовки, и программный флаг защищает от второго окна. Флаг сбрасывается в finally, чтобы отказ или сетевое исключение не оставили кнопку недоступной навсегда.

После успешного подключения сообщение не должно обещать, что человек вошёл в защищённый кабинет. Для серверной сессии потребуется отдельное сообщение с nonce, проверка подписи и cookie. Адрес, полученный через разрешение расширения, не является доказательством владения ключом для сервера. Этот следующий механизм рассматривается в отдельном курсе, не подменяя смысл нынешней кнопки.

Оглавление серии