китаев.tech

Что такое ABI

ABI в Ethereum: как TypeScript-код понимает функции смарт-контракта, кодирует calldata и читает ответы через RPC.

Что такое ABI

ABI — это описание интерфейса смарт-контракта.

Код контракта в сети хранится как bytecode. ABI объясняет вашему приложению, какие функции есть у контракта, какие аргументы они принимают и как декодировать ответ.


Зачем нужен ABI

Без ABI приложение видит только адрес и байткод:

text
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
0x608060405260043610...

С ABI этот же адрес превращается в понятный объект:

text
weth.name()
weth.symbol()
weth.decimals()
weth.balanceOf(address)

Ethereum не хранит имена функций в удобном виде для frontend или backend. ABI добавляет этот слой понимания на стороне вашего кода.


Читаем WETH через ABI

typescript
import { ethers } from "ethers";

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

const wethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

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

const weth = new ethers.Contract(wethAddress, wethAbi, provider);

const name = await weth.name();
const symbol = await weth.symbol();
const decimals = await weth.decimals();
const balance = await weth.balanceOf(vitalikAddress);

console.log("Name:", name);
console.log("Symbol:", symbol);
console.log("Decimals:", decimals.toString());
console.log("Balance:", ethers.formatUnits(balance, decimals));

Этот код вызывает реальные функции WETH-контракта в Ethereum mainnet. Без ABI ethers не узнал бы, как собрать запрос к balanceOf(address) и как прочитать uint256 из ответа.


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

wethAbi — минимальный ABI. В нём только те функции, которые нужны скрипту.

new ethers.Contract(wethAddress, wethAbi, provider) — создаёт объект контракта по адресу, ABI и RPC-провайдеру.

weth.balanceOf(vitalikAddress)ethers берёт описание balanceOf(address), кодирует аргумент и отправляет eth_call.

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


ABI превращается в calldata

Когда вы вызываете функцию контракта, Ethereum получает не имя функции, а байты в поле data.

typescript
import { ethers } from "ethers";

const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

const abi = ["function balanceOf(address owner) view returns (uint256)"];
const iface = new ethers.Interface(abi);

const calldata = iface.encodeFunctionData("balanceOf", [vitalikAddress]);

console.log(calldata);

Результат начинается с function selector:

text
0x70a08231...

0x70a08231 — первые 4 байта от хеша сигнатуры balanceOf(address). После них идут закодированные аргументы функции.


Что бывает в ABI

ЭлементЗачем нужен
functionвызвать или прочитать функцию контракта
eventдекодировать логи из receipt
constructorописать аргументы деплоя
errorдекодировать custom error при revert

Для чтения баланса ERC-20 не нужен полный ABI токена. Достаточно одной строки balanceOf(address).


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

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

Думать, что ABI хранится в блокчейне → в сети хранится bytecode. ABI обычно берут из исходников, артефактов компиляции или explorer API.

Использовать полный ABI без необходимости → для простого чтения можно передать только нужные функции. Это короче и легче проверять.


Что дальше

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