Skip to content

Using Ethers.js to Send Transactions on Moonbeam

Introduction

This guide walks through the process of using ethers.js to manually sign and send a transaction to a Moonbeam standalone node. For this example, we will use Node.js and straightforward JavaScript code.

The guide assumes that you have a local Moonbeam node running in --dev mode. You can find instructions to set up a local Moonbeam node here.

Note

This tutorial was created using the v3 release of Moonbase Alpha. The Moonbeam platform, and the Frontier components it relies on for Substrate-based Ethereum compatibility, are still under very active development. The examples in this guide assume an Ubuntu 18.04-based environment and will need to be adapted accordingly for MacOS or Windows.

Checking Prerequisites

If you followed this tutorial, you should have a standalone Moonbeam node producing blocks in your local environment, that looks like this:

Moonbeam local node

In addition, we need to install Node.js (we'll go for v15.x) and the npm package manager. You can do this by running in your terminal:

curl -sL https://deb.nodesource.com/setup_15.x | sudo -E bash -
sudo apt install -y nodejs

We can verify that everything installed correctly by querying the version for each package:

node -v
npm -v

As of the writing of this guide, versions used were 15.2.1 and 7.0.8, respectively.

Next, we can create a directory to store all our relevant files (in a separate path from the local Moonbeam node files) by running:

mkdir transaction && cd transaction/

And create a simple package.json file:

npm init --yes

With the package.json file created, we can then install the ethers.js library by executing:

npm install ethers

To verify the installed version of ethers.js, you can use the ls command:

npm ls ethers

As of the writing of this guide, the version used was 5.0.22.

The Transaction File

Similarly to our web3.js transaction tutorial, we only need a single JavaScript file (arbitrarily named transaction.js, which you can find here) to create and send the transaction, which we will run using the node command in the terminal. The script will transfer 100 ETH from the genesis account to another address. For simplicity, the file is divided into two sections: variable definition and deploy transaction.

In the first section we need to:

  1. Define the privKey variable as the private key of our genesis account, which is where all the funds are stored when initiating your local Moonbeam node, and what is used to sign the transactions. In ethers.js we need to set the prefix 0x
  2. Define the addressTo, for example to one created by MetaMask when setting up a local wallet
  3. Define our provider by passing in the RPC URL of our standalone Moonbeam node: http://localhost:9933
  4. Define our wallet by passing in the privKey and the provider. The wallet has the methods necessary to send the transaction

Note

Remember to change the addressTo variable to another address provided by your MetaMask wallet.

const ethers = require('ethers');

// Variables definition
const privKey =
   '0x99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342';
const addressTo = 'ADDRESSTO';
const providerURL = 'http://localhost:9933';
// Define Provider
let provider = new ethers.providers.JsonRpcProvider(providerURL);
// Create Wallet
let wallet = new ethers.Wallet(privKey, provider);

Next, we need an asynchronous function that wraps the wallet.sendTransaction(txObject) method. The transaction object is quite simple, it only requires the address to where we want to send the tokens and the amount to send. Note that we use the ethers.utils.parseEther() which handles the necessary unit conversions from Ether to Wei, this is similar to using ethers.utils.parseUnits(value,'ether'). Once the transaction is sent, we get the transaction response named createReceipt in this example, which has a few properties. For example, we can call the createReceipt.wait() method to wait until the transaction is processed.

// 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('100'),
   };

   const createReceipt = await wallet.sendTransaction(tx);
   await createReceipt.wait();
   console.log(`Transaction successful with hash: ${createReceipt.hash}`);
};

To wrap up, our completes transaction.js script looks like this:

const ethers = require('ethers');

// Variables definition
const privKey =
   '0x99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342';
const addressTo = 'ADDRESSTO';
const providerURL = 'http://localhost:9933';
// Define Provider
let provider = new ethers.providers.JsonRpcProvider(providerURL);
// Create Wallet
let wallet = new ethers.Wallet(privKey, provider);

// 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('100'),
   };

   const createReceipt = await wallet.sendTransaction(tx);
   await createReceipt.wait();
   console.log(`Transaction successful with hash: ${createReceipt.hash}`);
};

send();

The Balance File

As done in previous examples, we need another file to check the balances of both addresses before and after the transaction is executed. We can easily do this by leveraging the Ethereum compatibility features of Moonbeam.

For simplicity, the balance file (named arbitrarily balances.js, which you can find here), is composed of two sections: the variables definition and the balance call. The variables definition is nearly the same as for the previous transaction file; the only difference is that we do not need to define a wallet, as this is only a call function (reading data from the local Moonbeam node).

To get the balances of our addresses, we need to make an asynchronous function that uses the provider.getBalance(address) command. We can take advantage of the ethers.utils.formatEther() function to transform the balance into a more readable number in ETH, as we did before.

So basically, our balances.js script looks like this:

const ethers = require('ethers');

// Variables definition
const addressFrom = 'ADDRESSFROM';
const addressTo = 'ADDRESSTO';
const providerURL = 'http://localhost:9933';
// Define Provider
let provider = new ethers.providers.JsonRpcProvider(providerURL);

// Balance call
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();

Running the Scripts

First, let's check the balances of both of our addresses before the transaction by running:

node balances.js

The output of the execution is the following:

Balances before transaction

We can run our transaction.js script from the terminal window:

node transaction.js

The output of the execution is the following:

Deploy transaction

And we can check the new balances:

Balances after transaction

We Want to Hear From You

This is a fairly simple example, but it provides context for how you can start working with Moonbeam and how you can try out its Ethereum compatibility features. We are interested in hearing about your experience following the steps in this guide or your experience trying other Ethereum-based tools with Moonbeam. Feel free to join us in the Moonbeam Discord here. We would love to hear your feedback on Moonbeam and answer any questions that you have.