
В прошлых статьях (первая и вторая) я рассказывал вам, как подключиться к локальному блокчейну. Мы использовали Ganache, это ethereum-блокчейн, состоящий всего из одной ноды. Все транзакции происходили внутри нашего локального компьютера. Ganache идеально подходит для тестирования и отладки, так как уже после старта мы получаем предустановленные аккаунты со средствами на балансе, а результаты выполнения транзакций получаем моментально. Кроме того, это изолированный блокчейн и никто другой не может вмешаться в процесс автономного тестирования.
Помимо локальных тестовых блокчейнов существуют ещё и тестовые ethereum-сети, которые практически ничем не отличается от основной сети Mainnet. Здесь уже мы имеем дело с настоящей распределённой средой со множеством нод и независимыми аккаунтами. Транзакции здесь проходят аналогичный с основной сетью путь: распределение по нодам, валидация, включение в блок, достижение консенсуса между нодами относительного этого блока, и наконец, финализация - то есть приобретение транзакцией статуса неизменяемой и неотменяемой в блокчейне. Для записи транзакции в блокчейн требуется некоторое время.
Есть несколько тестовых ethereum сетей, вот некоторые из них:
Sepolia
Goerli
Mumbai
В данной статье я подключусь к Sepolia testnet, но в дальнейшем вы может использовать и другие сети.
Что мы будем делать?
Создадим окружение, зайдём в консоль и проверим подключение
Создадим аккаунты отправителя и получателя
Пополним аккаунт отправителя через Fauсet
Отправим Ether с одного аккаунта на другой
Зайдём на Etherscan и убедимся, что всё прошло корректно
Итак, приступим!
Шаг 1. Создаём окружение, заходим в консоль, проверяем подключение
Создадим рабочий каталог и установим в него web3.js
$ mkdir testnet-demo $ cd testnet-demo $ npm install web3
Зайдём в папку проекта и запустим node.js
$ node
Теперь установим RPC подключение к Sepolia testnet. Адрес подключения я взял отсюда.
> const Web3 = require("web3"); > const web3 = new Web3("https://rpc2.sepolia.org");
Проверим подключение:
> web3.currentProvider
Вывод:
HttpProvider { withCredentials: undefined, timeout: 0, headers: undefined, agent: undefined, connected: false, host: 'https://rpc2.sepolia.org', httpsAgent: Agent { _events: [Object: null prototype] { free: [Function (anonymous)], newListener: [Function: maybeEnableKeylog] }, _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype] { keepAlive: true, noDelay: true, path: null }, requests: [Object: null prototype] {}, sockets: [Object: null prototype] {}, freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: true, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 0, maxCachedSessions: 100, _sessionCache: { map: {}, list: [] }, [Symbol(kCapture)]: false } }
Для интереса можем запросить текущую стоимость одной единицы Gas:
> await web3.eth.getGasPrice(); // Out: '1000000006'
Отлично, соединение установлено, теперь создадим аккаунты участников транзакции.
Шаг 2. Создаём аккаунты отправителя и получателя
Создадим аккаунт отправителя:
> web3.eth.accounts.create();
Вывод:
{ address: '0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474', privateKey: '0x3a...e8', signTransaction: [Function: signTransaction], sign: [Function: sign], encrypt: [Function: encrypt] }
Теперь убедимся, что аккаунт действительно создан. Для этого зайдём на главную страницу тестовой сети, и в строке введём адрес аккаунта:

После этого мы попадем на страницу нашего аккаунта:

Аналогично создадим аккаунт получателя:
> web3.eth.accounts.create();
Вывод:
{ address: '0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0', privateKey: '0xab...80', signTransaction: [Function: signTransaction], sign: [Function: sign], encrypt: [Function: encrypt] }
Отлично, мы создали наших участников и убедились, что они отображаются в тестовой сети. Теперь давайте пополним баланс отправителя.
Шаг 3. Пополняем аккаунт отправителя через Fauсet
Для тестовых сетей существует и тестовая криптовалюта, получить её можно на ресурсах, называемых Faucet, или по-русски кран. Для каждой тестовой сети есть свой Faucet. Так как мы находимся в сети Sepolia, то нам сюда.
Перед выполнением операции вам скорее всего потребуется создать свою учётную запись. После того как вы залогинились, берём наш адрес отправителя:
0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474
И копируем его в строку. Подтверждаем, что вы человек и нажимаем Send Me ETH

После появится заставка, закрываем её и готово.

Мы зачислили себе 0.5 Ether. На момент написания статьи на Sepolia Faucet стояло ограничение в 0.5 Ether в день. Несмотря на кажущееся маленькое значение, это довольно-таки крупная сумма, и её вполне хватит для проведения тестовых транзакций.
Проверим наш аккаунт:

Замечательно! На балансе 0.5 Ether, а в списке транзакций видим перевод этих средств с Faucet на наш аккаунт.
Можно зайти и в саму транзакцию:

Здесь мы видим, что средства перевелись с аккаунта-донора на наш аккаунт. Если мы зайдём на аккаунт-донор, то увидим множество транзакций по 0.5 Ether. Это другие участники сети получают тестовые Ether.
Итак, у нас есть два участника, средства на балансе, а значит всё готово для отправки транзакции.
Шаг 4. Отправляем Ether с одного аккаунта на другой
Перед отправкой транзакции добавляем приватный ключ отправителя в наш локальный кошелёк, чтобы метод sendTransaction() смог найти его и подписать транзакцию. Для этого выполним в консоли следующую команду:
> web3.eth.accounts.wallet.add('0x3a...e8');
Дело в том, что в отличие от локального блокчейна Ganache, в тестовой сети возвращаемый приватный ключ нигде не сохраняется, поэтому мы должны сохранить его вручную.
Замечательно. Теперь у нас всё готово для вызова метода sendTransaction(), которой подпишет нашу транзакцию и передаст её в тестовую сеть Sepolia. В теле транзакции я добавил ещё поле gasLimit с минимально необходимым значением 21000. Если этого не сделать, то поле будет установлено в 0, а тестовая сеть отклонит транзакцию, так как нода-валидатор не сможет снять пошлину за обработку транзакции.
Отправляем транзакцию:
> await web3.eth.sendTransaction({from: '0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474', to: '0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0', value: web3.utils.toWei('0.01', 'ether'), gasLimit: 21000});
В ответ получаем квитанцию о проведённой транзакции:
{ blockHash: '0x6e9469710983d3b7be0e136cbff4d2dfa92e2c10fe926f502f0a1f67ad1f7de2', blockNumber: 3601461, contractAddress: null, cumulativeGasUsed: 425343, effectiveGasPrice: 2500000009, from: '0xc2325b2b3a8425ccb7c048ffa8f64e1d035ae474', gasUsed: 21000, logs: [], logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', status: true, to: '0xda6e32d495c0e70aed5f9e1e0d7dea8543fb37e0', transactionHash: '0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4', transactionIndex: 9, type: '0x2' }
В квитанции видим хэш транзакции и номер блока в который она попала.
Проверим из консоли балансы участников тарнзакции:
> await web3.eth.getBalance('0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474'); // Out: '489947499999811000' > await web3.eth.getBalance('0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0'); // Out: '10000000000000000'
Транзакция прошла успешно, и мы подошли к нашему финальному шагу - окончательной проверке полученных результатов.
Шаг 5. Заходим на Etherscan и убеждаемся, что всё прошло корректно
Открываем в Etherscan адреса отправителя и получателя


Отлично. Мы видим, что у наших участников изменились балансы, а в разделе транзакции появилось по одной новой транзакции, притом последняя транзакция имеет один и тот же хэш, так как она одна на двоих.
Мы можем зайти и в саму транзакцию, кликнув на её хэш в списке:

Из транзакции мы можем провалиться в блок, в который попала эта транзакция:

Ту же информацию мы можем получить и из консоли. Вот например информация о транзакции:
> await web3.eth.getTransaction('0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4');
Вывод:
{ blockHash: '0x6e9469710983d3b7be0e136cbff4d2dfa92e2c10fe926f502f0a1f67ad1f7de2', blockNumber: 3601461, from: '0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474', gas: 21000, gasPrice: '2500000009', maxFeePerGas: '2500000018', maxPriorityFeePerGas: '2500000000', hash: '0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4', input: '0x', nonce: 0, to: '0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0', transactionIndex: 9, value: '10000000000000000', type: 2, accessList: [], chainId: '0xaa36a7', v: '0x1', r: '0x2272a39cbfa9e75dac16d6488926b60090bf19d7a019246cf6d429ebd220a47a', s: '0x7369a5b91a4775c19e7e76d387c1e46e8ec8f44aa3b6992043b369f411da9452' }
А это информация о блоке:
> await web3.eth.getBlock('3601461');
Вывод:
{ baseFeePerGas: 9, difficulty: '0', extraData: '0x476f65726c69205365706f6c69612d4265706f6c696120513966', gasLimit: 30000000, gasUsed: 4924680, hash: '0x6e9469710983d3b7be0e136cbff4d2dfa92e2c10fe926f502f0a1f67ad1f7de2', logsBloom: '0x0001010002000000010080202010200601010008020000020c80010020806010000a00022020808000000010100008002000400000000000020040002024000802020002000020000c000009000102010031009050840000280202a000000400040080060602120000009004200009080a08880008480a0020040010408000510000400000000000210010000000080140020041000040400000020004400400a2880400002040100400400840080000104000400000000040102040000020140800000200200002020004200020102000000028400000088000000020806000041011080010002000a000200200002004000450040600015204c00000105084', miner: '0xFf58d746A67C2E42bCC07d6B3F58406E8837E883', mixHash: '0x3cec292e094271cfa845168c92c864a0ce630b8eee86adf2b26edba4cfb6da13', nonce: '0x0000000000000000', number: 3601461, parentHash: '0x4fdae93676cf69ac57bf3d70005f3f8cb7544364e15ccacfe9a48dcec2b10c97', receiptsRoot: '0xf90116292dfdcdb96e8d46b829744674d91eb8bc08413738735c9622180a806b', sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', size: 23359, stateRoot: '0x0b07d5b2d444bc2c9f960c9fc5b40754489826038a89665d9268137b6b16d95b', timestamp: 1685610816, totalDifficulty: '17000018015853232', transactions: [ '0xf0d3fc86a598811b1bff6452426eae007d8294c1b525974c07417ad479a64da2', '0x472fe3a00739eb596802ddd1f66f8ada2ebe9ba930390f98864d38e2b5045773', '0xd7c6479a2a08d25963532818e64e4ea64fe07c7d2b4b3eb5a80be0bcdba7acd7', '0xe32d2cb4f4c668773bc23e7d03552143ca3f35b14a9b099b6679c522da36adee', '0x200c8ce51b05490a192ed3b53f675f836d59f2dc714d1ca971b669ca90ef621a', '0xc334be29073dd4e181d7d4c481bf2f8b3b1206683876191277e6bc392e0642ac', '0xefc42a3b155790f9b64ffa4c663be4048e3226eea756fb530bedbb3c0272dd7d', '0x7fb223e9e809299b2bbf2a4dffa841cff32185a11d9dbf8e7dfeae6c8cd9b9a6', '0x8d2af3c0a4db0160539e8dec1790a893d993a65c355749d1917d9a2aac09a3e0', '0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4', '0xb6dfc9c9e9457cf81a8e0c990a8a1c16ef58b25f43d6d314c00ac58ea4a77ebe', '0x90ca55b8f73394593ea92343dd53bc2228abc732009540a629895a8cb38a07fd', '0x661c5ab4e3f854ce286f2fa0ee278596e03b1dea46854e18ca388b9c22c532c7', '0x25c649d23a5a5f9bf545c415c1cbfb256e3fac7fcb85f0913e1cd601e32abc0f', '0x28b0f7765b8c6897ddae5c32b92b34c216b7149d3b8a71725c300b3c752d71a6', '0x530160bed0a1f4ab410f1a20879b0663b99ec84fac34bcc5f22be6068070aecc', '0xa64aea5c59dd462131c941a85b0c7c2bbfabdf60b815b7535a8b5bef5bbeb74e', '0xb75f2cc6766ccb8bcb3aeab241e57cf4e330d8d9a50fb9bea463155552ce31b5', '0xe2af47f0d3b4ead18cef2693ecfbfeeec57004e57252be4c198165c3a4dcc411', '0x1f6d31ca35b033b4aae431a1b212b5d222844a35311b54f28a148394f6032be9', '0xe1bfe48f3f6f9cf1b2b491bb7a6193a308eb38d5095c229cb82a7e0ca96d7e1f', '0xf627b541b16e275de5f48aee01af49e6f9a69405bab08df1566845fb643c7807', '0xe52dc798e3ce48242b52046eee555654a84b8462161acb6e1f5223dfa9f5a135', '0x8eab69c265c28af2022d09d81e4587e1936d51ed0f8879f92fe469caeb79eec2', '0x698665b27b3b1b796d76e2d546cb3ba54fc7f716f63f1dee5524a7c57b2e2878', '0xe627ed697178499240fef79c49ffc17fa074c81323f35ce1fd67ae25dd71cfe2', '0xa846655a9e27a86c29da227f819124cd033078a51b0ac9155f38d1fb6a24e3a7', '0xd70ad44740445384004fc2f39d469fd809ffa2453f6e4c539b0d457499320655' ], transactionsRoot: '0x2df7c4fce57e1c56cf10d3c4b6c84c795c395821bb1c6e1f41810368f3a5b05f', uncles: [], withdrawals: [ { index: '0x94fa81', validatorIndex: '0x3b5', address: '0xe276bc378a527a8792b353cdca5b5e53263dfb9e', amount: '0x244aa' }, // ... Большой список ... { index: '0x94fa90', validatorIndex: '0x3c4', address: '0xe276bc378a527a8792b353cdca5b5e53263dfb9e', amount: '0x2f6aa' } ], withdrawalsRoot: '0x25c3a945376fba5742d93a83c818c0baccd6887164b93425a04fc926782cf937' }
Среди множества полей блока обратим внимание на поле transactions - это транзакции других участников, которые были добавлены в этот блок наряду с нашей транзакцией. Есть ещё интересное поле miner - это адрес ноды-валидатора, которая была выбрана для сборки блока. Если вы посмотрите этот адрес, то увидите сколько блоков этот валидатор добавил в блокчейн.
На этом всё. Далее вы можете самостоятельно поэкспериментировать с тестовой сетью, отправив в неё и другие вызовы из библиотеки web3
Поздравляю! Сегодня мы познакомились с тестовыми сетями, и научились взаимодействовать с ними посредством RPC-вызовов с помощью библиотеки web3.js и терминала. Все эти команды в дальнейшем вы можете объединить в JavaScript-файл и выполнять уже более сложные операции. Надеюсь информация была полезной :)
