Пост

Взаимодействие с Ethereum нодой

Взаимодействие с Ethereum нодой

По сути вся работа с блокчейном в PHP - это так или иначе взаимодействие с блокчейн нодой. Все подписанные транзакции отправляются (бродкастятся) на неё и все данные из блокчейна также можно получить у ноды. Обычно у ноды есть свой json-rpc интерфейс, через который с ней можно взаимодействовать.

Документация по JSON-RPC API Ethereum ноды.

Библиотека

Так как общение с нодой - это просто HTTP-запросы, то у нас есть два варианта. Можно либо использовать готовую библиотеку, которая формирует нужный payload и парсит ответы. Либо использовать любой HTTP-клиент и делать всё вручную. Рассмотрим вариант с уже готовой библиотекой Simple-Web3-Php:

1
composer require drlecks/simple-web3-php

И попробуем подключиться к публичной ноде https://eth.public-rpc.com и узнать номер текущего блока в блокчейне:

1
2
3
4
use SWeb3\SWeb3;

$node = new SWeb3('https://eth.public-rpc.com');
$res = $node->call('eth_blockNumber', []);

Метод call(string $method, $params = null) позволяет вызывать json-rpc метод у ноды. В нашем случае вызываем eth_blocknumber, у которого нет параметров. В ответ получим объект stdClass с телом json-rpc ответа:

1
2
3
4
5
6
stdClass Object
(
    [id] => 1
    [jsonrpc] => 2.0
    [result] => 0x14fcf95
)

Нам интересно поле result, но важно помнить, что данные нода вернет в 16-ом формате. Поэтому, чтобы получить номер блока в читаемом виде - нужно будет явно его привести в 10-ую систему:

1
2
3
4
5
6
7
8
9
use SWeb3\SWeb3;
use SWeb3\Utils;

$node = new SWeb3('https://eth.public-rpc.com');
$res = $node->call('eth_blockNumber', []);
$block = Utils::hexToBn($res->result);
echo 'Block number: ' . $block->toString() . PHP_EOL;

// Block number: 22008077

Получение баланса

Теперь попробуем получить баланс (eth_getBalance()) какого-нибудь адреса (0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97:

1
2
3
4
5
$res = $node->call('eth_getBalance', ['0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97', 'latest']);
$balance = Utils::hexToBn($res->result);
echo 'Balance: ' . $balance->toString() . PHP_EOL;

// Balance: 16857795621056046838

В результате получаем число, которое отличается от, того что мы видим на эксплорере в ETH BALANCE:

Почему так происходит? Дело в том, что нода возвращает балансы в wei (наименьший номинал ETH).

1 ETH = 1,000,000,000,000,000,000 WEI.

Чтобы из Wei получить ETH нужно явно привести полученное число wei в eth:

1
2
3
4
5
$res = $node->call('eth_getBalance', ['0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97', 'latest']);
$balance = Utils::hexToBn($res->result);
echo 'Balance: ' . Utils::fromWeiToString($balance, 'ether') . PHP_EOL;

// Balance: 16.857795621056046838

Теперь уже будет больше похоже на правду.

Генерация аккаунта

Мало смысла в работе с блокчейном, не имея своего собственного адреса, поэтому давайте создадим себе адрес (публичный и приватный ключ):

1
2
3
4
5
6
7
8
9
10
use SWeb3\Accounts;

$account = Accounts::create();
echo "Public key: " . $account->publicKey . PHP_EOL;
echo "Private key: " . $account->privateKey . PHP_EOL;
echo "Address: " . $account->address . PHP_EOL;

// Public key: 0x03c484a1c224a35100310228205c40af86a0a511e8ad41ef71470c86b23e25fbfd
// Private key: 0x65e2bda31ccefd2851ef69b268e62a9e00773cfa5506ae5525d3435d4589f2d1
// Address: 0x0e1cd3dc5ceb15f2a515b5cefbcd80ceb935f38d

Теперь попробуем получить баланс нашего адреса и ожидаемо получим 0:

1
2
3
4
5
$res = $node->call('eth_getBalance', [$account->address, 'latest']);
$balance = Utils::hexToBn($res->result);
echo 'Balance: ' . Utils::fromWeiToString($balance, 'ether') . PHP_EOL;

// Balance: 0

После того как научились коннектиться к ноде, понимаем как делать к ней запросы и парсить ответы — можно переходить к более сложным вещам: отправке ETH и взаимодействию со смарт-контрактами.