How to send Ether to an Ethereum account: web3.js vs ethers.js

Rosario Borgesi
Coinmonks
7 min readMay 11, 2024

--

In this comprehensive tutorial, we’ll delve into the process of programmatically issuing transactions and transferring Ether between Ethereum accounts. Our toolkit will include the powerful libraries ethers.js and web3.js, both harnessed within a Typescript environment. To facilitate our interactions with the Ethereum network, we will rely on the Infura provider as our gateway. Let’s kick off this exciting journey!

Table Of Contents

· Table Of Contents
· Ethers.js And Web3.js
· Infura Provider
· What you need
· Sending Ether with Web3.js
· Sending Ether with Ethers.js
· Github Repository
· Further Exploration
· Conclusions
· References

Ethers.js And Web3.js

To engage with the Ethereum blockchain in a programmatic manner, developers have the option to utilize either the web3.js or ethers.js libraries. Both libraries provide a comprehensive set of tools that facilitate communication with the Ethereum network, allowing for a wide range of operations such as sending transactions, interacting with smart contracts, and querying blockchain data.

Web3.js is one of the original JavaScript libraries for Ethereum and is widely used due to its robust features and extensive documentation. It offers a convenient interface for interacting with the Ethereum Virtual Machine (EVM), Ethereum nodes, and other blockchain elements.

On the other hand, ethers.js is a newer library that has gained popularity for its lightweight, modular structure and ease of use. It is designed to be more intuitive and user-friendly, with a focus on security and simplicity.

Both libraries support asynchronous JavaScript operations, smart contract interaction, and various Ethereum functionalities. Choosing between web3.js and ethers.js depends on the specific needs of the project, the preferences of the development team, and the complexity of the tasks at hand. Ultimately, both are excellent choices for developers looking to build decentralized applications (DApps) or other blockchain-related projects.

Infura Provider

To establish a connection with the Ethereum blockchain, developers can utilize a service known as a “provider,” which is offered by various companies, including Infura. A provider acts as a bridge, linking users to an Ethereum node hosted on the blockchain. This connection is crucial for performing operations such as executing transactions, deploying smart contracts, and querying blockchain data.

The advantage of using a provider like Infura is that it eliminates the need for individuals or developers to run their own Ethereum node locally. Setting up and maintaining a node can be resource-intensive and costly, requiring significant computational power, storage, and bandwidth. By using a provider, developers can access the blockchain network seamlessly and more cost-effectively.

Infura, specifically, offers an API key to its users. This key serves as a provider, granting access to various Ethereum networks such as Mainnet, Ropsten, Rinkeby, Goerli, and Sepolia. With this API key, developers can connect to the network of their choice and perform a wide array of operations without the overhead of managing a full node. Infura’s service is particularly beneficial for developers building decentralized applications (DApps) or conducting blockchain research, as it provides a reliable and scalable infrastructure for interacting with the Ethereum ecosystem.

What you need

We will execute the code in the Sepolia test network but the process is similar to operate in the mainnet. You will need:

Sending Ether with Web3.js

Let’s create a new npm project with:

npm init -y

Then let’s install the web3.js library with:

npm install web3

Then let’s install Typescript:

npm install -D typescript

In the root project let’s add this tsconfig.json:

{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"rootDir": "src",
"outDir": "dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true
}
}

Now let’s install the dotenv library to simplify the proces of managing environment variables in our application.

npm install -D dotenv

In the root of our project we can create a .env file to store the project and Ethereum account details:

ETHEREUM_NETWORK = "sepolia"
INFURA_API_KEY = "<Your-API-Key>"
SIGNER_PRIVATE_KEY = "<Your-Private-Key>"

If using a network other than Sepolia, ensure you update ETHEREUM_NETWORK with the network name.

In the package.json file I have also added the script main:

"scripts": {
"main": "tsc && node dist/main.js"
},

That allows us to compile the code in Javascript and then execute it by executing:

npm run main

This will be our package.json file:

{
"name": "send_ether_web3_ethers",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"main": "tsc && node dist/main.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"web3": "^4.8.0"
},
"devDependencies": {
"dotenv": "^16.4.5",
"typescript": "^5.4.5"
}
}

Then I have added the source code main.ts in a package src:

import { Web3 } from "web3";
import { ETH_DATA_FORMAT } from "web3";
import 'dotenv/config'

const main = async () => {
// Configuring the connection to an Ethereum node
const network = process.env.ETHEREUM_NETWORK;
const web3 = new Web3(
new Web3.providers.HttpProvider(
process.env.INFURA_API_KEY ?? "",
),
);
// Creating a signing account from a private key
const signer = web3.eth.accounts.privateKeyToAccount(
"0x" + process.env.SIGNER_PRIVATE_KEY,
);
web3.eth.accounts.wallet.add(signer);

const limit = await web3.eth
.estimateGas(
{
from: signer.address,
to: "0x350a97Aa777CcfE518197C34342C5bA262825B35",
value: web3.utils.toWei("0.0001", "ether"),
},
"latest",
ETH_DATA_FORMAT,
);

// Creating the transaction object
const tx = {
from: signer.address,
to: "0x350a97Aa777CcfE518197C34342C5bA262825B35",
value: web3.utils.toWei("0.0001", "ether"),
gas: limit,
nonce: await web3.eth.getTransactionCount(signer.address),
maxPriorityFeePerGas: web3.utils.toWei("3", "gwei"),
maxFeePerGas: web3.utils.toWei("300", "gwei"),
chainId: 11155111,
type: 0x2,
};
const signedTx = await web3.eth.accounts.signTransaction(tx, signer.privateKey);
console.log("Raw transaction data: " + signedTx.rawTransaction);
// Sending the transaction to the network
const receipt = await web3.eth
.sendSignedTransaction(signedTx.rawTransaction)
.once("transactionHash", (txhash) => {
console.log(`Mining transaction ...`);
console.log(`https://${network}.etherscan.io/tx/${txhash}`);
});
// The transaction is now on chain!
console.log(`Mined in block ${receipt.blockNumber}`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
})

We are sending 0.0001 Ether from my first Metamask account signer.address which is 0x1F0c72E13718D9136FfE51b89289b239A1BcfE28, to my second account 0x350a97Aa777CcfE518197C34342C5bA262825B35.

If we run: npm run main it prints:

Mining transaction ...
https://sepolia.etherscan.io/tx/0x25246c52bdf7cb5a95484a0366a9f72286d7ba119c5c9b3e4130cff2d0b2b7d0
Mined in block 5881520

And we can verify the transaction on Etherscan:

  • From: is signer.address and it is my first Metamask account: 0x1F0c72E13718D9136FfE51b89289b239A1BcfE28
  • To: is the address to which we have send the Ethere, which is 0x350a97Aa777CcfE518197C34342C5bA262825B35
  • Value: it is the value we have sent 0.0001 ETH

You can find the complete code on Github.

Sending Ether with Ethers.js

The process is very similar to the previous one. Let’s create a new npm project:

npm init -y

Then install ethers.js:

npm install ethers

Then install Typescript and dotenv:

npm install -D typescript dotenv

In the root project let’s add the sametsconfig.jsonas before.

Let’s also create a .env file with the following properties:

INFURA_PROJECT_SECRET = "<your_infura_project_secret>"
INFURA_PROJECT_ID = "<your_infura_project_id>"
SIGNER_PRIVATE_KEY = "<your_ethereum_account_private_key>"

The two properties INFURA_PROJECT_SECRETand INFURA_PROJECT_IDcan be retrieved from the Infura dashboard and you don’t need to create them because they have been created when you registered your API key.

The SIGNER_PRIVATE_KEY is the private key of your Metamask account.

Also in this case we can modify the package.jsonfile, by some scripts:

{
"name": "send_ether_web3_ethers",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"basic": "tsc && node dist/basic.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"ethers": "^6.12.1"
},
"devDependencies": {
"dotenv": "^16.4.5",
"typescript": "^5.4.5"
}
}

Now let’s create a folder src and then inside of it a new file basic.ts:

import ethers, { InfuraProvider, Wallet, parseUnits } from "ethers";
import 'dotenv/config'

const main = async () => {
// Configuring the connection to an Ethereum node
const network: String = "sepolia";
const provider = new InfuraProvider(
"sepolia",
process.env.INFURA_PROJECT_ID,
process.env.INFURA_PROJECT_SECRET
);
// Creating a signing account from a private key
const signer = new Wallet(process.env.SIGNER_PRIVATE_KEY ?? "", provider);

// Creating and sending the transaction object
const tx = await signer.sendTransaction({
to: "0x350a97Aa777CcfE518197C34342C5bA262825B35",
value: parseUnits("0.001", "ether"),
});
console.log("Mining transaction...");
console.log(`https://${network}.etherscan.io/tx/${tx.hash}`);
// Waiting for the transaction to be mined
const receipt = await tx.wait();
// The transaction is now on chain!
if(receipt != null){
console.log(`Mined in block ${receipt.blockNumber}`);
}
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
})

In this script we send 0.001 Ether from the account 0x1F0c72E13718D9136FfE51b89289b239A1BcfE28 to the account 0x350a97Aa777CcfE518197C34342C5bA262825B35.
If you run npm run basicyou should something like:

Mining transaction...
https://sepolia.etherscan.io/tx/0x4387efb0f06591f1d9e05be1d5dd27aa879d09464584d6b11068289c0dc098bb
Mined in block 5881993

Also in this case it is possible to verify the transaction on Etherscan:

It is also possible to fine tune the transaction details by following this example. You can find the code on Github.

Github Repository

The code for the 2 examples with both web3.js and ethers.js can be found at this Github repository.

Further Exploration

For those eager to dive into coding Solidity smart contracts, I recommend exploring the following resources:

For those who want to understand more about how the blockchain works I can recommend the following article:

Conclusions

In this article, we’ve delved into the fascinating world of programmatically sending Ether to an Ethereum account using both ethers.js and web3.js. By leveraging these powerful libraries, you can automate transactions, interact with smart contracts, and explore the Ethereum blockchain programmatically.

Whether you’re a seasoned developer or just starting your journey into decentralized applications (DApps), understanding how to send Ether programmatically is a crucial skill. We’ve covered the essential steps, from setting up your environment to executing transactions, all while harnessing the capabilities of these libraries.

I hope this article serves as a valuable resource for you. If you have any questions or need further clarification, please don’t hesitate to leave a comment. Happy coding, and may your Ethereum adventures be both rewarding and enlightening!

References

--

--