Użycie Bibliotek Ethereum aby Wysłać Transakcje na Moonbeam
Wstęp
Ten przewodnik opisuje wykorzystanie trzech różnych bibliotek Ethereum do ręcznego podpisywania i wysyłania transakcji na Moonbeam. Trzy biblioteki omówione w tym samouczku to:
W przykładach tego przewodnika założono, że masz środowisko oparte na systemie MacOS lub Ubuntu 18.04 i należy je odpowiednio dostosować w przypadku systemu Windows.
Wymagania Wstępne
Przykłady wykorzystujące zarówno web3.js, jak i ethers.js wymagają wcześniejszej instalacji Node.js i NPM. Przykład z użyciem web3.py wymaga Pythona i PIP. W chwili pisania tego przewodnika używane były następujące wersje:
- Node.js v15.10.0
- NPM v7.5.3
- Python v3.6.9 (web3 requires Python >= 3.5.3 and < 4)
- PIP3 v9.0.1
Następnie utwórz katalog, w którym będą przechowywane wszystkie odpowiednie pliki:
mkdir transaction && cd transaction/
W przypadku bibliotek JavaScript możesz najpierw utworzyć prosty plik package.json (niewymagany):
npm init --yes
W katalogu zainstaluj bibliotekę, która ma być używana (web3.py jest instalowany w domyślnym katalogu PIP3):
Web3.js
npm i web3
Ethers.js
npm i ethers
Web3.py
pip3 install web3
Wersje używane na dzień publikacji tego przewodnika to:
- Web3.js v1.33 (
npm ls web3
) - Ethers.js v5.0.31 (
npm ls ethers
) - Web3.py v5.17.0 (
pip3 show web3
)
Plik Transakcji
Do wykonania transakcji pomiędzy rachunkami potrzebny jest tylko jeden plik. Skrypt pokazany w tej sekcji przeniesie 1 Token z adresu pochodzenia (z którego posiadasz klucz prywatny) na inny adres. Fragment kodu dla każdej biblioteki można znaleźć tutaj (zostały one arbitralnie nazwane transaction.*):
- Web3.js: transaction.js
- Ethers.js: transaction.js
- Web3.py: transaction.py
Każdy z plików, niezależnie od użytej biblioteki, został podzielony na trzy sekcje. W pierwszej sekcji (“Define Provider & Variables”) biblioteka do użycia jest importowana, a dostawca i inne zmienne są definiowane (zmienne zależą od biblioteki). Należy zauważyć, że providerRPC ma zarówno standardowy węzeł programistyczny RPC, jak i punkt końcowy dla Moonbase Alpha.
Druga sekcja (“Create and Deploy Transaction”) przedstawia funkcje potrzebne do wysłania samej transakcji. Niektóre z kluczowych wniosków zostaną omówione poniżej.
Web3.js
const Web3 = require('web3'); /*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: 'http://localhost:9933',
moonbase: 'https://rpc.testnet.moonbeam.network',
};
// Change to correct network
const web3 = new Web3(providerRPC.development); const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
address: 'PUBLIC-ADDRESS-OF-PK-HERE',
};
const addressTo = 'ADDRESS-TO-HERE'; // Change addressTo /*
-- Create and Deploy Transaction --
*/
const deploy = async () => {
console.log(
`Attempting to send transaction from ${account_from.address} to ${addressTo}`
); // Sign Tx with PK
const createTransaction = await web3.eth.accounts.signTransaction( {
gas: 21000,
to: addressTo,
value: web3.utils.toWei('1', 'ether'),
},
account_from.privateKey
); // Send Tx and Wait for Receipt
const createReceipt = await web3.eth.sendSignedTransaction( createTransaction.rawTransaction
);
console.log(
`Transaction successful with hash: ${createReceipt.transactionHash}`
);
}; deploy();
Ether.js
const ethers = require('ethers'); /*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: {
name: 'moonbeam-development',
rpc: 'http://localhost:9933',
chainId: 1281,
},
moonbase: {
name: 'moonbase-alpha',
rpc: 'https://rpc.testnet.moonbeam.network',
chainId: 1287,
},
};
const provider = new ethers.providers.StaticJsonRpcProvider( providerRPC.development.rpc,
{
chainId: providerRPC.development.chainId,
name: providerRPC.development.name,
}
); //Change to correct network // Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
};
const addressTo = 'ADDRESS-TO-HERE'; // Create Wallet
let wallet = new ethers.Wallet(account_from.privateKey, provider); /*
-- Create and Deploy Transaction --
*/
const send = async () => {
console.log(
`Attempting to send transaction from ${wallet.address} to ${addressTo}`
); // Create Tx Object
const tx = {
to: addressTo,
value: ethers.utils.parseEther('1'),
}; // Sign and Send Tx - Wait for Receipt
const createReceipt = await wallet.sendTransaction(tx);
await createReceipt.wait();
console.log(`Transaction successful with hash: ${createReceipt.hash}`);
}; send();
Web3.py
from web3 import Web3 #
# -- Define Provider & Variables --
#
# Provider
provider_rpc = {
"development": "http://localhost:9933",
"alphanet": "https://rpc.testnet.moonbeam.network",
}
# Change to correct network
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"])) # Variables
account_from = {
"private_key": "YOUR-PRIVATE-KEY-HERE",
"address": "PUBLIC-ADDRESS-OF-PK-HERE",
}
address_to = "ADDRESS-TO-HERE" # Change address_to #
# -- Create and Deploy Transaction --
#
print(
f'Attempting to send transaction from { account_from["address"] } to { address_to }'
) # Sign Tx with PK
tx_create = web3.eth.account.signTransaction(
{
"nonce": web3.eth.getTransactionCount(account_from["address"]), "gasPrice": 0,
"gas": 21000,
"to": address_to,
"value": web3.toWei("1", "ether"),
},
account_from["private_key"],
) # Send Tx and Wait for Receipt
tx_hash = web3.eth.sendRawTransaction(tx_create.rawTransaction) tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash) print(f"Transaction successful with hash:
{ tx_receipt.transactionHash.hex() }")
Web3.js
W pierwszej sekcji skryptu instancja web3 (lub dostawca) jest tworzona przy użyciu konstruktora Web3 z dostawcą RPC. Zmieniając dostawcę RPC podanego konstruktorowi, możesz wybrać, do której sieci chcesz wysłać transakcję.
Klucz prywatny i powiązany z nim adres publiczny są zdefiniowane odpowiednio do podpisywania transakcji i logowania. Wymagany jest tylko klucz prywatny.
W tym miejscu zdefiniowany jest również adressTo, na który zostanie wysłana transakcja, i jest on wymagany.
W drugiej sekcji tworzony jest obiekt transakcji z polami to, value i gas. Opisują one odbiorcę, kwotę do wysłania oraz gaz zużyty przez transakcję (w tym przypadku 21000). Możesz użyć funkcji web3.utils.toWei(), aby wprowadzić wartość w ETH (na przykład) i uzyskać dane wyjściowe w WEI. Transakcja jest podpisana kluczem prywatnym za pomocą metody web3.eth.accounts.signTransaction(). Zauważ, że zwraca to obietnicę, którą należy rozwiązać.
Następnie, z podpisaną transakcją (możesz console.log(createTransaction), aby zobaczyć wartości v-r-s), możesz wysłać ją za pomocą metody web3.eth.sendSignedTransaction(), podając podpisaną transakcję znajdującą się w createTransaction.rawTransaction.
Na koniec uruchom funkcję asynchroniczną wdrażania.
Ethers.js
W pierwszej sekcji skryptu można określić różne sieci za pomocą nazwy, adresu URL RPC (wymagane) i identyfikatora łańcucha. Dostawca (podobny do instancji web3) jest tworzony za pomocą metody ethers.providers.StaticJsonRpcProvider. Alternatywą jest użycie metody ethers.providers.JsonRpcProvide(providerRPC), która wymaga tylko adresu punktu końcowego RPC dostawcy. Mogło to jednak powodować problemy ze zgodnością ze specyfikacjami poszczególnych projektów.
Klucz prywatny jest zdefiniowany w celu utworzenia instancji portfela, co również wymaga dostawcy z poprzedniego kroku. Instancja portfela służy do podpisywania transakcji.
W tym miejscu zdefiniowany jest również adressTo, na który zostanie wysłana transakcja, i jest on wymagany.
W drugiej sekcji funkcja asynchroniczna otacza metodę wallet.sendTransaction(txObject). Obiekt transakcji jest dość prosty. Wymaga jedynie adresu odbiorcy i kwoty do wysłania. Zauważ, że można użyć metody ethers.utils.parseEther(), która obsługuje niezbędne konwersje jednostek z ETH na WEI — podobnie jak przy użyciu metody ethers.utils.parseUnits(value,’ether’).
Po wysłaniu transakcji możesz uzyskać odpowiedź dotyczącą transakcji (o nazwie createReceipt w tym przykładzie), która ma kilka właściwości. Na przykład można wywołać metodę createReceipt.wait(), aby zaczekać na przetworzenie transakcji (status paragonu to OK).
Na koniec uruchom funkcję wdrażania asynchronicznego.
Web3.py
W pierwszej sekcji skryptu instancja web3 (lub dostawca) jest tworzona przy użyciu metody Web3(Web3.HTTPProvider(provider_rpc)) z dostawcą RPC. Zmieniając dostawcę RPC, możesz wybrać sieć, do której chcesz wysłać transakcję.
Klucz prywatny i powiązany z nim adres publiczny są definiowane do podpisywania transakcji i logowania. Adres publiczny nie jest wymagany.
W tym miejscu zdefiniowany jest również adressTo, na który zostanie wysłana transakcja, i jest on wymagany.
W drugiej sekcji tworzony jest obiekt transakcji z polami nonce, gasPrice, gas, to i value. Opisują one liczbę transakcji, cenę gazu (0 dla rozwoju i Bazy Księżycowej Alpha), gaz (w tym przypadku 21000), odbiorcę i kwotę do wysłania. Należy pamiętać, że licznik transakcji można uzyskać za pomocą metody web3.eth.getTransactionCount(adres). Możesz również użyć funkcji web3.toWei(), aby wprowadzić wartość w ETH (na przykład) i uzyskać dane wyjściowe w WEI. Transakcja jest podpisana kluczem prywatnym za pomocą metody web3.eth.account.signTransaction().
Następnie z podpisaną transakcją można ją wysłać za pomocą metody web3.eth.sendSignedTransaction(), podając podpisaną transakcję znajdującą się w createTransaction.rawTransaction.
Saldo Pliku
Przed uruchomieniem skryptu inny plik sprawdza salda obu adresów przed i po transakcji. Można to łatwo zrobić za pomocą prostego zapytania o saldo konta.
Fragment kodu dla każdej biblioteki można znaleźć tutaj (pliki zostały dowolnie nazwane balances.*):
- Web3.js: balances.js
- Ethers.js: balances.js
- Web3.py: balances.py
Dla uproszczenia plik bilansu składa się z dwóch części. Tak jak poprzednio, w pierwszej sekcji (“Define Provider & Variables”), importowana jest biblioteka do użycia oraz definiowany jest dostawca i adres od/do (w celu sprawdzenia sald).
Druga sekcja (“Balance Call Function”) przedstawia funkcje potrzebne do pobrania sald wcześniej zdefiniowanych rachunków. Należy zauważyć, że providerRPC ma zarówno standardowy node development RPC, jak i endpoint dla Moonbase Alpha. Niektóre z kluczowych wniosków zostaną omówione poniżej.
Web3.js
const Web3 = require('web3'); /*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: 'http://localhost:9933',
moonbase: 'https://rpc.testnet.moonbeam.network',
};
//Change to correct network
const web3 = new Web3(providerRPC.development); // Variables
const addressFrom = 'ADDRESS-FROM-HERE';
const addressTo = 'ADDRESS-TO-HERE'; /*
-- Balance Call Function --
*/
const balances = async () => {
const balanceFrom = web3.utils.fromWei(
await web3.eth.getBalance(addressFrom),
'ether'
);
const balanceTo = web3.utils.fromWei(
await web3.eth.getBalance(addressTo),
'ether'
); console.log(`The balance of ${addressFrom} is: ${balanceFrom} ETH`);
console.log(`The balance of ${addressTo} is: ${balanceTo} ETH`); }; balances();
Ethers.js
const ethers = require('ethers'); /*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: {
name: 'moonbeam-development',
rpc: 'http://localhost:9933',
chainId: 1281,
},
moonbase: {
name: 'moonbase-alpha',
rpc: 'https://rpc.testnet.moonbeam.network',
chainId: 1287,
},
};
const provider = new ethers.providers.StaticJsonRpcProvider( providerRPC.development.rpc,
{
chainId: providerRPC.development.chainId,
name: providerRPC.development.name,
}
); //Change to correct network // Variables
const addressFrom = 'ADDRESS-FROM-HERE';
const addressTo = 'ADDRESS-TO-HERE'; /*
-- Balance Call Function --
*/
const balances = async () => {
const balanceFrom = ethers.utils.formatEther(
await provider.getBalance(addressFrom)
); const balanceTo = ethers.utils.formatEther(
await provider.getBalance(addressTo)
); console.log(`The balance of ${addressFrom} is: ${balanceFrom} ETH`);
console.log(`The balance of ${addressTo} is: ${balanceTo} ETH`); }; balances();
Web3.py
from web3 import Web3 #
# -- Define Provider & Variables --
#
# Provider
provider_rpc = {
"development": "http://localhost:9933",
"alphanet": "https://rpc.testnet.moonbeam.network",
}
# Change to correct network
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"])) # Variables
address_from = "ADDRESS-FROM-HERE"
address_to = "ADDRESS-TO-HERE" #
# -- Balance Call Function --
#
balance_from = web3.fromWei(web3.eth.getBalance(address_from), "ether")
balance_to = web3.fromWei(web3.eth.getBalance(address_to), "ether")
print(f"The balance of { address_from } is: { balance_from } ETH") print(f"The balance of { address_to } is: { balance_to } ETH")
Web3.js
Pierwsza sekcja skryptu jest bardzo podobna do tej w pliku transakcji. Główna różnica polega na tym, że nie jest potrzebny klucz prywatny, ponieważ nie ma potrzeby wysyłania transakcji.
W drugiej sekcji funkcja asynchroniczna otacza metodę web3 używaną do pobierania salda adresu web3.eth.getBalance(adres). Po raz kolejny możesz wykorzystać funkcję web3.utils.fromWei(), aby przekształcić saldo w bardziej czytelną liczbę w ETH.
Ethers.js
Pierwsza sekcja skryptu jest bardzo podobna do tej w pliku transakcji. Główna różnica polega na tym, że nie jest potrzebny klucz prywatny, ponieważ nie ma potrzeby wysyłania transakcji. Wręcz przeciwnie, należy zdefiniować adressFrom.
W drugiej sekcji funkcja asynchroniczna otacza metodę dostawcy używaną do pobierania salda adresu, czyli provider.getBalance(address). Po raz kolejny możesz wykorzystać funkcję ethers.utils.formatEther(), aby przekształcić saldo w bardziej czytelną liczbę w ETH.
Web3.py
Pierwsza sekcja skryptu jest bardzo podobna do tej w pliku transakcji. Główna różnica polega na tym, że nie jest potrzebny klucz prywatny, ponieważ nie ma potrzeby wysyłania transakcji.
W drugiej sekcji metoda web3.eth.getBalance(adres) służy do pobierania salda adresu docelowego. Po raz kolejny możesz wykorzystać funkcję eb3.fromWei(), aby przekształcić saldo w bardziej czytelną liczbę w ETH.
Uruchamianie Skryptów
W tej sekcji pokazany wcześniej kod został dostosowany do docelowego węzła programistycznego, który można uruchomić, postępując zgodnie z tym samouczkiem. Ponadto każda transakcja została wysłana z konta z przedpłatą, które pochodzi z węzła:
- klucz prywatny:
99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342
- klucz publiczny:
0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b
sajpierw sprawdź salda obu adresów przed transakcją, uruchamiając (zwróć uwagę, że nazwa katalogu została zmieniona dla każdej biblioteki):
Web3.js
node balances.js
Ethers.js
node balances.js
Web3.py
python3 balances.py
Następnie uruchom skrypt transaction.*, aby wykonać transakcję:
Web3.js
node transaction.js
Ethers.js
node transaction.js
Web3.py
python3 transaction.py
I na koniec, ponownie sprawdź saldo, aby upewnić się, że transfer się powiódł. Całość powinna wyglądać tak:
Web3.js
Ethers.js
Web3.py