Submitting transactions is a core task when interacting with blockchains, like peaq. Transactions include transferring funds, interacting with smart contracts, or deploying contracts. A transaction is required each time we want to write data to the blockchain and it’s the responsibility of the developer to construct the transaction call data. This guide provides a structured approach to submitting transactions using ethers.js, ensuring efficiency and security.

Prerequisites

Before following the instructions, ensure the following:

  1. Environment Setup:
    • Node.js and npm (or pnpm/yarn) are installed.
    • Ethers.js library is installed: npm install ethers.
  2. Account Setup:
    • You have access to a private key or a connected wallet (e.g., MetaMask).
    • Your wallet has sufficient funds for gas fees.
  3. Blockchain Setup:
    • You know the URL of the RPC you’re interacting with.
    • You understand the transaction details, including recipient address, value, and any data (if applicable).
  4. Security Awareness:
    • Never expose your private key or mnemonic in your code.
    • Use environment variables for sensitive information.

Instructions

1. Install and Import ethers.js

In your JavaScript file, import ethers.js:

import { ethers } from 'ethers';

2. Set Up a Provider

Connect to an Ethereum-compatible network:

const provider = new ethers.JsonRpcProvider("https://peaq.api.onfinality.io/public");

3. Configure a Signer

Create a wallet instance to sign transactions:

const privateKey = process.env.PRIVATE_KEY; //Please store private keys in environment variables
const wallet = new ethers.Wallet(privateKey, provider);

4. Define the Transaction

Set up the transaction details:

const tx = {
  to: "0xRecipientAddressHere", // Replace with the recipient's address
  value: ethers.parseEther("0.1"), // Amount in ETH to send (converted from Ether)
  gasLimit: 21000, // Gas limit (for simple transfers)
  maxFeePerGas: ethers.parseUnits("20", "gwei"), // Max fee per gas
  maxPriorityFeePerGas: ethers.parseUnits("2", "gwei"), // Priority fee
};

For interacting with smart contracts, include data in the transaction object.

5. Sign and Submit the Transaction

Sign and send the transaction:

async function sendTransaction() {
  try {
    const txResponse = await wallet.sendTransaction(tx);
    console.log("Transaction submitted:", txResponse.hash);
    const receipt = await txResponse.wait();
    console.log("Transaction mined:", receipt);
  } catch (error) {
    console.error("Error submitting transaction:", error);
  }
}

sendTransaction();

6. Verify the Transaction

Monitor the transaction using the txResponse.hash in a block explorer or programmatically check its status using the provider.getTransactionReceipt method.

Notes

  • Gas Estimation: For more complex transactions, calculate gas estimates using contract.estimateGas.methodName().
  • Smart Contracts: For interacting with contracts, set up the contract instance using its ABI and address:
const contract = new ethers.Contract(contractAddress, contractABI, wallet);

Putting it all Together

Here’s a complete boilerplate script that ties together all the steps for submitting a transaction using ethers.js. This script includes environment variable handling, transaction setup, and submission.

// Import ethers.js
import { ethers } from 'ethers';
require("dotenv").config(); // To use environment variables

// Boilerplate Script
async function main() {
  try {
    // Step 1: Set up the provider
    const rpcUrl = process.env.RPC_URL; // RPC URL for your blockchain
    if (!rpcUrl) throw new Error("RPC_URL is not defined in .env");
    const provider = new ethers.JsonRpcProvider(rpcUrl);

    // Step 2: Configure the wallet (signer)
    const privateKey = process.env.PRIVATE_KEY; // Private key stored in environment variables
    if (!privateKey) throw new Error("PRIVATE_KEY is not defined in .env");
    const wallet = new ethers.Wallet(privateKey, provider);

    console.log(`Connected wallet: ${wallet.address}`);

    // Step 3: Define the transaction
    const tx = {
      to: "0xRecipientAddressHere", // Replace with the recipient's address
      value: ethers.parseEther("0.1"), // Amount in ETH to send (e.g., 0.1 ETH)
      gasLimit: 21000, // Standard gas limit for a simple ETH transfer
      maxFeePerGas: ethers.parseUnits("20", "gwei"), // Max fee per gas
      maxPriorityFeePerGas: ethers.parseUnits("2", "gwei"), // Priority fee
    };

    console.log("Prepared transaction:", tx);

    // Step 4: Sign and send the transaction
    console.log("Sending transaction...");
    const txResponse = await wallet.sendTransaction(tx);
    console.log(`Transaction submitted: ${txResponse.hash}`);

    // Step 5: Wait for transaction to be mined
    console.log("Waiting for transaction to be mined...");
    const receipt = await txResponse.wait();
    console.log("Transaction mined:", receipt);

    console.log(`Transaction successful! Block Number: ${receipt.blockNumber}`);
  } catch (error) {
    console.error("Error occurred:", error.message);
  }
}

// Execute the script
main();

Instructions for Using the Boilerplate Script

  1. Install Dependencies: Ensure you have the required packages installed:
npm install ethers dotenv
  1. Set ESM Module: Add the following to your package.json to alllow for ESM modules.
"type": "module",
  1. Set Up Environment Variables: Create a .env file in the root directory of your project with the following keys:
RPC_URL=https://peaq.api.onfinality.io/public //Can be replaced with your chosen RPC
PRIVATE_KEY=your-private-key
  1. Run the Script: Execute the script in your terminal:
node script.js
  1. Modify Transaction Details:
    • Replace 0xRecipientAddressHere with the recipient’s address.
    • Adjust value, maxFeePerGas, and maxPriorityFeePerGas as needed.