Moonbeam
July 21, 2021

Verwendung von Ethereum-Bibliotheken für die Sendung der Transaktionen auf Moonbeam

Einführung

Diese Anleitung erklärt die Verwendung von drei verschiedenen Ethereum-Bibliotheken, um eine Transaktion auf Moonbeam manuell zu signieren und zu senden. Die drei Bibliotheken, die in diesem Tutorial behandelt werden, sind:

Hinweis In den Beispielen wird davon ausgegangen, dass Sie eine auf MacOS oder Ubuntu 18.04 basierende Umgebung haben. Für Windows müssen die Beispiele entsprechend angepasst werden .

Voraussetzungen prüfen

Die Beispiele, die sowohl web3.js als auch ethers.js verwenden, erfordern eine vorherige Installation von Node.js und NPM. Das Beispiel mit web3.py erfordert Python und PIP. Zum Zeitpunkt der Erstellung dieser Anleitung wurden folgende Versionen verwendet:

  • Node.js v15.10.0
  • NPM v7.5.3
  • Python v3.6.9 (web3 erfordert Python >= 3.5.3 and < 4)
  • PIP3 v9.0.1

Erstellen Sie ein Verzeichnis, um alle relevanten Dateien zu speichern:

mkdir transaction && cd transaction/

Für die JavaScript-Bibliotheken können Sie zunächst eine einfache package.json Datei erstellen (nicht erforderlich):

npm init --yes

Installieren Sie im Verzeichnis die zu verwendende Bibliothek (web3.py wird im Standardverzeichnis von PIP3 installiert):

Web3.js

 npm i web3

Ethers.js

 npm i ethers

Web3.py

 pip3 install web3

Die, zum Zeitpunkt der Veröffentlichung dieser Anleitzng, verwendeten Versionen waren:

  • Web3.js v1.33 (npm ls web3)
  • Ethers.js v5.0.31 (npm ls ethers)
  • Web3.py v5.17.0 (pip3 show web3)

Die Transaktionsdatei

Es wird nur eine Datei benötigt, um eine Transaktion zwischen Noden auszuführen. Das in diesem Abschnitt gezeigte Skript überträgt 1 Token von einer Ursprungsadresse (von der Sie den privaten Schlüssel besitzen) an eine andere Adresse. Den Code-Schnipsel für jede Bibliothek finden Sie hier (sie wurden willkürlich transaction.*genannt):

Jede der Dateien, unabhängig von der verwendeten Bibliothek, wurde in drei Abschnitte unterteilt. Im ersten Abschnitt ("Define Provider & Variables") wird die zu verwendende Bibliothek importiert und der Provider sowie weitere Variablen definiert (Variablen sind bibliotheksabhängig). Beachten Sie, dass providerRPC sowohl den Standard-development node RPC-endpoint als auch den für Moonbase Alpha hat.

Der zweite Abschnitt ("Create and Deploy Transaction") beschreibt die Funktionen, die zum Senden der Transaktion selbst erforderlich sind.

Web3.js

const Web3 = require('web3');

/*
   -- Define Provider & Variables --
*/
// Provider
const providerRPC = {
   development: 'http://localhost:9933',
   moonbase: 'https://rpc.testnet.moonbeam.network',
};
const web3 = new Web3(providerRPC.development); //Change to correct network

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();

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 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",
}
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"]))  # Change to correct network

# 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

Im ersten Abschnitt des Skripts wird die web3-Instanz (oder der Anbieter (provider)) mithilfe des Web3-Konstruktors mit dem provider RPC erstellt. Indem Sie den Provider-RPC ändern, der dem Konstruktor übergeben wurde, können Sie auswählen, an welches Netzwerk Sie die Transaktion senden möchten.

Der private Schlüssel (private key) und die damit verbundene public Adresse werden zur Signierung der Transaktion bzw. zur Protokollierung (logging) definiert. Es wird nur der private Schlüssel benötigt.

Die addressTo, an die die Transaktion gesendet wird, wird ebenfalls hier definiert und ist erforderlich.

Im zweiten Abschnitt wird das Transaktionsobjekt mit den Feldern to, value, und gas angelegt. Diese beschreiben den Empfänger, den zu sendenden Betrag und den Gasverbrauch der Transaktion (in diesem Fall 21000). Sie können die Funktion web3.utils.toWei() verwenden, um den Wert (zum Beispiel) in ETH einzugeben und die Ausgabe in WEI zu erhalten. Die Transaktion wird mit dem private key unter Verwendung der Funktion web3.eth.accounts.signTransaction() signiert. Beachten Sie, dass dies ein Promise zurückgibt, das aufgelöst werden muss.

Als Nächstes können Sie die signierte Transaktion (Sie können console.log(createTransaction) verwenden, um die v-r-s-Werte anzuzeigen) mit der Funktion web3.eth.sendSignedTransaction() senden und die signierte Transaktion bereitstellen, die sich in createTransaction.rawTransaction befindet.

Führen Sie abschließend die asynchrone Bereitstellungsfunktion (deploy function) aus.

Ethers.js

Im ersten Abschnitt des Skripts können verschiedene Netzwerke mit einem Namen, einer RPC-URL (erforderlich) und einer Chain-ID angegeben werden. Der Provider (ähnlich der web3Instanz) wird mit der Funktion ethers.providers.StaticJsonRpcProvider erstellt. Eine Alternative: die Funktion ethers.providers.JsonRpcProvide(providerRPC) zu verwenden, die nur die RPC-endpoint address des Anbieters erfordert. Dies kann jedoch zu Kompatibilitätsproblemen mit einzelnen Projektspezifikationen führen.

Private key wird definiert, um eine Wallet-Instanz zu erstellen, die auch den Anbieter aus dem vorherigen Schritt benötigt. Die Wallet-Instanz wird verwendet, um Transaktionen zu signieren.

addressTo, an die die Transaktion gesendet wird, wird ebenfalls hier definiert und ist erforderlich.

Im zweiten Abschnitt wickelt (wraps) eine asynchrone Funktion die Methode wallet.sendTransaction(txObject). Das Transaktionsobjekt ist recht einfach. Es benötigt nur die Adresse des Empfängers und den zu sendenden Betrag. Beachten Sie, dass ethers.utils.parseEther() verwendet werden kann, das die notwendigen Einheitenumrechnungen von ETH in WEI durchführt - ähnlich wie bei der Verwendung von ethers.utils.parseUnits(value,'ether').

Sobald die Transaktion gesendet wurde, können Sie die Transaktionsantwort (transaction response ) (in diesem Beispiel mit dem Namen createReceipt) abrufen, die einige Eigenschaften aufweist. Sie können beispielsweise die Methode createReceipt.wait() aufrufen, um zu warten, bis die Transaktion verarbeitet wurde (receipt status is OK).

Führen Sie abschließend die asynchrone Bereitstellungsfunktion (deploy function) aus.

Web3.py

Im ersten Abschnitt des Skripts wird die web3 Instanz (oder der Provider) mithilfe der Web3(Web3.HTTPProvider(provider_rpc))Methode mit dem Provider-RPC erstellt. Durch die Änderung des Anbieter-RPC können Sie auswählen, an welches Netzwerk Sie die Transaktion senden möchten.

Private key und die damit verbundene public address werden zur Signierung der Transaktion und zu Protokollierung (logging) definiert. Public address ist nicht erforderlich.

addressTo, an die die Transaktion gesendet wird, wird ebenfalls hier definiert und ist erforderlich.

Im zweiten Abschnitt wird das Transaktionsobjekt mit den Feldern nonce, gasPrice, gas, to, und value angelegt. Diese beschreiben die Transaktionsanzahl, den Gaspreis (0 für development und Moonbase Alpha), Gas (in diesem Fall 21000), den Empfänger (recipient) und den zu sendenden Betrag. Beachten Sie, dass die Transaktionsanzahl mit der Methode web3.eth.getTransactionCount(address) abgerufen werden kann. Sie können auch die Funktion web3.toWei() verwenden, um den Wert (zum Beispiel) in ETH einzugeben und die Ausgabe in WEI zu erhalten. Die Transaktion wird mit private key unter Verwendung der Methode web3.eth.account.signTransaction() signiert.

Als Nächstes können Sie die signierte Transaktion mit der Methode web3.eth.sendSignedTransaction() senden, indem Sie die signierte Transaktion in createTransaction.rawTransaction bereitstellen.

Die Bilanzdatei (The Balance File)

Bevor das Skript ausgeführt wird, überprüft eine andere Datei die Salden beider Adressen, bevor und nach der Transaktion. Dies kann ganz einfach durch eine einfache Abfrage eines Kontostands erfolgen.

Den Codeausschnitt für jede Bibliothek finden Sie hier (Dateien wurden balances.* genannt):

Die Bilanzdatei besteht aus zwei Abschnitten. Nach wie vor wird im ersten Abschnitt („Provider & Variablen definieren“) die zu verwendende Bibliothek importiert und der Provider und die Adresse von/nach (um die Salden zu prüfen) definiert.

Der zweite Abschnitt ("Balance Call Function") beschreibt die Funktionen, die zum Abrufen der Salden der zuvor definierten Konten (accounts) erforderlich sind. Beachten Sie, dass providerRPC sowohl Standard-development node RPC endpoint als auch endpoint für Moonbase Alpha hat.

Web3.js

const Web3 = require('web3');

/*
   -- Define Provider & Variables --
*/
// Provider
const providerRPC = {
   development: 'http://localhost:9933',
   moonbase: 'https://rpc.testnet.moonbeam.network',
};
const web3 = new Web3(providerRPC.development); //Change to correct network

// 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",
}
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"]))  # Change to correct network

# 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

Der erste Abschnitt des Skripts ist dem, in der Transaktionsdatei, sehr ähnlich. Der Hauptunterschied besteht darin, dass kein private key benötigt wird, da keine Transaktion gesendet werden muss.

Im zweiten Abschnitt umwickelt (wraps) eine asynchrone Funktion die web3 Methode, die zum Abrufen des Saldos einer Adresse verwendet wird, web3.eth.getBalance(address). Auch hier können Sie die Funktion web3.utils.fromWei() nutzen, um den Saldo in eine besser lesbare Zahl in ETH umzuwandeln.

Ethers.js

Der erste Abschnitt des Skripts ist dem, in der Transaktionsdatei, sehr ähnlich. Der Hauptunterschied besteht darin, dass kein private key benötigt wird, da keine Transaktion gesendet werden muss. Im Gegenteil, addressFrom muss definiert werden.

Im zweiten Abschnitt umwickelt (wraps) eine asynchrone Funktion die Provider-Methode, die zum Abrufen des Saldos einer Adresse verwendet wird, nämlich provider.getBalance(address). Auch hier können Sie die Funktion ethers.utils.formatEther() nutzen, um den Saldo in eine besser lesbare Zahl in ETH umzuwandeln.

Web3.py

Der erste Abschnitt des Skripts ist dem, in der Transaktionsdatei, sehr ähnlich. Der Hauptunterschied besteht darin, dass kein private key benötigt wird, da keine Transaktion gesendet werden muss.

Im zweiten Abschnitt wird die Methode web3.eth.getBalance(address) verwendet, um den Saldo einer Zieladresse abzurufen. Auch hier können Sie die Funktion eb3.fromWei() nutzen, um den Saldo in eine besser lesbare Zahl in ETH umzuwandeln.

Skriptausführung

Für diesen Abschnitt wurde der zuvor gezeigte Code so angepasst, dass er für die Ausführung des development node verwendet werden kann. Bitte, dieser Anleitung folgen. Außerdem wurde jede Transaktion von dem vorfinanzierten Konto (pre-funded account) gesendet, das mit dem Noden geliefert wird:

  • Private key:

99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342

  • Public address:

0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b

Überprüfen Sie zunächst die Salden beider Adressen vor der Transaktion (beachten Sie, dass das Verzeichnis für jede Bibliothek umbenannt wurde). Führen Sie bitte aus:

Web3.js

 node balances.js

Ethers.js

 node balances.js

Web3.py

 python3 balances.py

Führen Sie als Nächstes das Skript Transaction.* aus, um die Transaktion auszuführen:

Web3.js

 node transaction.js

Ethers.js

 node transaction.js

Web3.py

 python3 transaction.py

Überprüfen Sie abschließend noch einmal das Guthaben, um sicherzustellen, dass die Überweisung erfolgreich war. Es soll so aussehen:

Web3.js

Ethers.js

Web3.py

Original (engl.)