китаев.tech

Настройка publicClient и walletClient

Создашь publicClient и walletClient в viem, подключишь MetaMask через custom(window.ethereum) и прочитаешь баланс адреса.

Два клиента вместо Provider и Signer

В viem чтение сети и действия от имени аккаунта разведены на два объекта: publicClient и walletClient.

publicClient ходит в RPC и читает данные — баланс, блоки, eth_call. walletClient общается с кошельком: запрашивает адреса, подписывает сообщения и отправляет транзакции. В ethers это примерно JsonRpcProvider / BrowserProvider и Signer.


Создаём оба клиента и подключаем кошелёк

Код рассчитан на браузер с установленным MetaMask (или другим EIP-1193 кошельком).

typescript
import "viem/window";
import {
  createPublicClient,
  createWalletClient,
  custom,
  http,
  formatEther,
} from "viem";
import { mainnet } from "viem/chains";

const publicClient = createPublicClient({
  chain: mainnet,
  transport: http("https://ethereum.publicnode.com"),
});

if (!window.ethereum) {
  throw new Error("Кошелёк не найден. Установи MetaMask или другой EIP-1193 провайдер.");
}

const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum),
});

// Открывает окно кошелька и запрашивает доступ к адресам
const [address] = await walletClient.requestAddresses();

const balance = await publicClient.getBalance({ address });
const chainId = await publicClient.getChainId();

console.log("Адрес:", address);
console.log("Сеть (chainId):", chainId);
console.log("Баланс:", formatEther(balance), "ETH");

Чтение баланса идёт через HTTP-RPC, а не через MetaMask. Кошелёк нужен только чтобы узнать адрес и позже подписывать действия.


Как это работает

createPublicClient({ chain, transport }) — клиент только для чтения. Ему не нужен приватный ключ и не нужен window.ethereum.

http("https://...") — транспорт к своей RPC-ноде. Для продакшена лучше Alchemy/Infura/свой URL, а не публичный endpoint.

createWalletClient({ chain, transport: custom(...) }) — клиент подписи. custom(window.ethereum) оборачивает провайдер расширения в transport viem.

requestAddresses() — аналог eth_requestAccounts: показывает окно кошелька и возвращает разрешённые адреса. Без этого шага getAddresses() часто вернёт пустой массив.

publicClient.getBalance({ address }) — баланс читаешь через public-клиент, даже если адрес пришёл из wallet-клиента. Так viem разделяет «кто читает» и «кто подписывает».


Если ты знаешь ethers.js

ethers v6viem
new JsonRpcProvider(url)createPublicClient({ transport: http(url) })
new BrowserProvider(window.ethereum)createWalletClient({ transport: custom(window.ethereum) })
provider.send("eth_requestAccounts", [])walletClient.requestAddresses()
provider.getSigner()signer.getAddress()адрес сразу из requestAddresses() / getAddresses()
Один объект часто и читает, и подписываетЧтение и подпись — разные клиенты по умолчанию

В ethers BrowserProvider часто используют и для RPC, и для подписи. В viem привычнее держать HTTP-RPC отдельно, а кошелёк — только для account/sign/send.


Частые ошибки

Один клиент «на всё»walletClient умеет часть read-методов через кошелёк, но для стабильного чтения (баланс, логи, multicall) бери publicClient + свой RPC.

getAddresses() вместо requestAddresses()getAddresses не всегда открывает окно подключения. Для первого коннекта нужен requestAddresses().

Нет проверки window.ethereum → в Node.js и в браузере без расширения код упадёт. Проверяй провайдер до custom(window.ethereum).


Что дальше

Материалы китаev.tech публикуются в образовательных целях и не являются инвестиционной рекомендацией. Примеры кода и описания протоколов — для обучения; использование в продакшне на ваш собственный риск. Дисклеймер и политика конфиденциальности.