Эфириум в Расте? Часть первая: Отправка простых транзакций.
В этом руководстве показано, как запустить локальную виртуальную машину 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