Эфириум в Расте? Часть первая: Отправка простых транзакций.
В этом руководстве показано, как запустить локальную виртуальную машину Ethereum (EVM)
в Rust, запросить баланс и совершить простые транзакции.
Настройка зависимостей
Прежде чем писать код, мы должны убедиться, что у нас установлены некоторые необходимые инструменты, а именно Rust
и Ganache
.
Rust
Установите Rust, следуя инструкциям
. Или, если вы используете Arch Linux, вы можете следовать инструкциям здесь
, чтобы установить Rust.
ganache-cli
Ganache
поможет вам быстро создать среду Ethereum для тестирования. Если вам не нужен весь пакет, вы можете просто установить ganache-cli
с помощью npm,
npm install -g ganache
Запустите ganache-cli
, чтобы проверить, правильно ли вы его установили.
Настройка проекта Rust
Теперь мы создадим проект Rust и добавим необходимые зависимости.
Создайте папку нашего проекта и инициализируйте его с помощью cargo
:
mkdir rust-ethereum-tutorial cd rust-ethereum-tutorial cargo init
Для удобства добавления зависимостей Rust в наш проект мы будем использовать cargo-edit
:
cargo install cargo-edit
Это добавит последнюю версию ethers
в качестве зависимости в Cargo.toml
:
cargo add --no-default-features ethers +legacy
cargo add tokio +full clap hex eyre
Краткое описание этих библиотек:
tokio
- позволяет нам использовать асинхронные функции в Rustclap
- для работы с аргументами командной строкиhex
- для работы с шестнадцатеричными строкамиeyre
- это библиотека, которая помогает сократить количество кодового кода при обработке ошибок
Подключение к RPC Ethereum Web3
Узел Ethereum
обычно предоставляет конечную точку HTTP, WebSocket или IPC для доступа к сети Ethereum
. Для разработки в локальной среде мы можем подключиться к нашей конечной точке, предоставляемой локально запущенным ganache. Чтобы запустить ganache
в Rust, мы можем просто использовать Ganache::new().spawn()
:
use ethers::utils::Ganache; use eyre::Result;#[tokio::main] async fn main() -> Result<()> { // Спавн Ganache экземпляра let ganache = Ganache::new().spawn(); println!("HTTP Endpoint: {}", ganache.endpoint()); Ok(()) }
Если мы выполним команду cargo run
, мы увидим URL
конечной точки, выведенный в консоли:
... HTTP Endpoint: http://localhost:46795
Доступ к тестовым кошелькам ganache
При каждом запуске Ganache будет использовать случайно генерируемые мнемоники.
Чтобы сделать наш пример более детерминированным, мы можем настроить его на использование определенную мнемонику:
let mnemonic = "gas monster ski craft below illegal discover limit dog bundle bus artefact"; let ganache = Ganache::new().mnemonic(mnemonic).spawn();
Чтобы получить доступ к кошельку, созданному ganache, мы сначала получим закрытые ключи, а затем преобразуем их в экземпляр LocalWallet
:
// Получите первый кошелек, управляемый ganache let wallet: LocalWallet = ganache.keys()[0].clone().into(); let wallet_address: String = wallet.address().encode_hex(); println!("Default wallet address: {}", wallet_address);
Подключение через JSON RPC
Для взаимодействия с узлом или сетью Ethereum нам потребуется создать клиент, который подключается к конечной точке ganache:
// Провайдер - это клиент Ethereum JsonRPC. let provider = Provider::try_from(ganache.endpoint())?.interval(Duration::from_millis(10));
Запрос баланса по адресу
Теперь, когда наш клиент подключен к сети Ethereum, мы можем запросить баланс любого адреса в нашем кошельке:
// Запросить баланс нашего счета let first_balance = provider.get_balance(first_address, None).await?; println!("Баланс первого адреса кошелька: {}", first_balance);
Если мы снова запустим программу cargo run
, то узнаем, что по этому адресу у нас есть 1000ETH. Это добавляется программой Ganache автоматически.
Чтобы запросить баланс, используя адрес в формате шестнадцатеричной строки, нам нужно сначала преобразовать строку в тип Address:
// Запросить баланс случайного счета let other_address_hex = "0xaf206dCE72A0ef76643dfeDa34DB764E2126E646"; let other_address = "0xaf206dCE72A0ef76643dfeDa34DB764E2126E646".parse::<Address>()?; let other_balance = provider.get_balance(other_address, None).await?; println!( "Баланс адреса {}: {}", other_address_hex, other_balance );
Попробуйте cargo run
, и вы увидите, что баланс этого адреса равен нулю:
Баланс адреса 0xaf206dCE72A0ef76643dfeDa34DB764E2126E646: 0
Создать простую транзакцию
Далее мы создадим простую транзакцию для перевода нескольких токенов Ethereum на другой адрес.
// Создайте транзакцию для перевода 1000 wei на `other_address`. let tx = TransactionRequest::pay(other_address, U256::from(1000u64)).from(first_address); // Отправляем транзакцию и ждем получения let receipt = provider .send_transaction(tx, None) .await? .log_msg("Ожидание трансфера") .await? .context("Нет получателя")?; println!( "Транзакция замайнена на блоке {}", receipt.block_number.context("Не получилось получить блок транзакции")? ); println!( "Баланс у {} {}", other_address_hex, provider.get_balance(other_address, None).await? );
Выполните cargo run, вы получите следующие результаты:
Ожидание трансфера: 0xff6153310304732bb28856ca3a90ab9c94c5c9e20cf51e8a2803f3483670cd6f Транзакция замайнена на блоке 1 Баланс у 0xaf206dCE72A0ef76643dfeDa34DB764E2126E646 1000