китаев.tech

Вызов view-функций — read

Как читать данные из смарт-контракта через ethers v6: подключить ABI, вызвать view-функции и разобрать результат без транзакции.

Что такое read-вызов

Read-вызов читает состояние контракта и не меняет State Ethereum.

Для view и pure функций не нужен private key, signer или ETH на балансе. Достаточно RPC-провайдера, адреса контракта и ABI.


Читаем данные ERC-20 контракта

Создайте проект:

bash
mkdir call-view-functions-example
cd call-view-functions-example

npm init -y
npm pkg set type="module"
npm install ethers tsx typescript @types/node
mkdir scripts

Создайте файл scripts/read-usdc.ts:

typescript
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider("https://ethereum.publicnode.com");

const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

const erc20Abi = [
  "function name() view returns (string)",
  "function symbol() view returns (string)",
  "function decimals() view returns (uint8)",
  "function totalSupply() view returns (uint256)",
  "function balanceOf(address account) view returns (uint256)",
];

const usdc = new ethers.Contract(usdcAddress, erc20Abi, provider);

const [name, symbol, decimals, totalSupply, balance] = await Promise.all([
  usdc.name(),
  usdc.symbol(),
  usdc.decimals(),
  usdc.totalSupply(),
  usdc.balanceOf(vitalikAddress),
]);

console.log("Token:", name);
console.log("Symbol:", symbol);
console.log("Decimals:", decimals.toString());
console.log("Total supply:", ethers.formatUnits(totalSupply, decimals));
console.log("Vitalik balance:", ethers.formatUnits(balance, decimals), symbol);

Запустите скрипт:

bash
npx tsx scripts/read-usdc.ts

Код прочитает состояние реального USDC-контракта в Ethereum mainnet. Транзакция не отправляется, gas не списывается.


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

new ethers.JsonRpcProvider() — подключается к RPC-ноде Ethereum.

erc20Abi — минимальный ABI. В нём только функции, которые мы вызываем.

new ethers.Contract(usdcAddress, erc20Abi, provider) — создаёт read-only объект контракта.

usdc.name() — вызывает view функцию name() через RPC.

usdc.balanceOf(vitalikAddress) — кодирует аргумент address, отправляет eth_call и возвращает uint256.

ethers.formatUnits(balance, decimals) — переводит raw значение токена в читаемый формат с учётом decimals.

Promise.all([...]) — запускает независимые read-вызовы параллельно.


Что происходит под капотом

Когда вы вызываете usdc.balanceOf(...), ethers не отправляет транзакцию.

Он собирает RPC-запрос eth_call:

text
to:   0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
data: 0x70a08231...

Нода выполняет функцию локально на текущем State и возвращает результат. Блокчейн от этого не меняется.


Read vs write

Один и тот же контракт можно подключить по-разному:

typescript
const readOnlyUsdc = new ethers.Contract(usdcAddress, erc20Abi, provider);
const writableUsdc = new ethers.Contract(usdcAddress, erc20Abi, signer);

readOnlyUsdc.balanceOf(user) — бесплатный read-вызов через eth_call.

writableUsdc.transfer(to, amount) — транзакция, которая требует подпись и gas.


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

Искать gas fee у view-функции → обычный read-вызов не попадает в блок и не тратит gas. Gas нужен только для транзакций.

Подключать signer для чтения → для view функций достаточно provider. Signer нужен, когда функция меняет State.

Передавать неправильный ABI → адрес может быть верным, но ethers не сможет закодировать вызов, если ABI не содержит нужную функцию.

Забывать про decimals у токеновbalanceOf() возвращает raw uint256, а не человекочитаемое число.


Что дальше

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