Signing Raw Transactions

Some ways we can utilize raw transactions include signing a transaction offline on a secure machine and then broadcasting it separately or to send a transaction at a later point of time.

1. Setting up a Node.js project

In a new folder, initiate a new Node.js project with the command npm init -y to accept all default project parameters.

A package.json file will be created for you, which contains all your packages and project information.

2. Integrating Ethers.js

We'll need to integrate a helpful JavaScript library known as Ethers.js that will help with interacting with the Ethereum blockchain.

We can do so using the command npm i ethers from a terminal within this project, which will add ethers as a dependency.

We'll then create a new file named transaction.js where we'll write the rest of the code in JavaScript and import ethers at the top of the file.

const ethers = require('ethers');

async function main() {

  // rest of code goes here

}

main();

3. Creating a wallet

One of the useful classes that Ethers.js provides is a Wallet, which represents a regular Ethereum address that you can use to store and send Ether.

We can initiate a new Wallet by specifying a private key which we can generate or grab one from an existing wallet like MetaMask.****

const ethers = require('ethers');

async function main() {

  // defining the wallet private key
  let privatekey = 'CE75F1A875F2DB7FB064F5DBD302B0C77FFEAA18CC4C314167A5111A04F79AFA';
  let wallet = new ethers.Wallet(privatekey);

  // print the wallet address
  console.log('Using wallet address ' + wallet.address);
  
}

main();

4. Crafting the transaction

A transactions is made up of several parameters that have to be defined, for this example we'll be making a simple ETH transfer.

The sample code to send 1 Ether to address 0xEeee7341f206302f2216e39D715B96D8C6901A1C on the Ropsteh testnet will be as follows.

const ethers = require('ethers');

async function main() {

  let privatekey = 'CE75F1A875F2DB7FB064F5DBD302B0C77FFEAA18CC4C314167A5111A04F79AFA';
  let wallet = new ethers.Wallet(privatekey);

  console.log('Using wallet address ' + wallet.address);

  let transaction = {
    to: '0xa238b6008Bc2FBd9E386A5d4784511980cE504Cd',
    value: ethers.utils.parseEther('1'),
    gasLimit: '21000',
    maxPriorityFeePerGas: ethers.utils.parseUnits('5', 'gwei'),
    maxFeePerGas: ethers.utils.parseUnits('20', 'gwei'),
    nonce: 1,
    type: 2,
    chainId: 3
  };

}

main();

5. Sign & serialize

After detailing the contents of the transaction, we'll need to sign it — using the wallet's private key we created in Step 3 to prove we are allowed to spend funds in the wallet address.

With the transaction signed, we have just one more step to serialize, or simply converting our transaction into a hexadecimal format.

const ethers = require('ethers');

async function main() {

  let privatekey = 'CE75F1A875F2DB7FB064F5DBD302B0C77FFEAA18CC4C314167A5111A04F79AFA';
  let wallet = new ethers.Wallet(privatekey);

  console.log('Using wallet address ' + wallet.address);

  let transaction = {
    to: '0xa238b6008Bc2FBd9E386A5d4784511980cE504Cd',
    value: ethers.utils.parseEther('1'),
    gasLimit: '21000',
    maxPriorityFeePerGas: ethers.utils.parseUnits('5', 'gwei'),
    maxFeePerGas: ethers.utils.parseUnits('20', 'gwei'),
    nonce: 1,
    type: 2,
    chainId: 3
  };

  // sign and serialize the transaction 
  let rawTransaction = await wallet.signTransaction(transaction).then(ethers.utils.serializeTransaction(transaction));
  
  // print the raw transaction hash
  console.log('Raw txhash string ' + rawTransaction);

}

main();

6. Send via API

With the signed raw transaction, we can now pass it to the "eth_sendRawTransaction" endpoint to be broadcasted to the Ethereum network.

A successfully broadcasted transaction will return a transaction hash, which you can use the "eth_getTransactionbyHash" endpoint or look it up on Optimism Etherscan!

You can run this code using the command node transaction.js

const ethers = require('ethers');
const fetch = require('node-fetch');

async function main() {

  let privatekey = 'CE75F1A875F2DB7FB064F5DBD302B0C77FFEAA18CC4C314167A5111A04F79AFA';
  let wallet = new ethers.Wallet(privatekey);

  console.log('Using wallet address ' + wallet.address);

  let transaction = {
    to: '0xa238b6008Bc2FBd9E386A5d4784511980cE504Cd',
    value: ethers.utils.parseEther('1'),
    gasLimit: '21000',
    maxPriorityFeePerGas: ethers.utils.parseUnits('5', 'gwei'),
    maxFeePerGas: ethers.utils.parseUnits('20', 'gwei'),
    nonce: 1,
    type: 2,
    chainId: 3
  };

  let rawTransaction = await wallet.signTransaction(transaction).then(ethers.utils.serializeTransaction(transaction));
  console.log('Raw txhash string ' + rawTransaction);

  // pass the raw transaction hash to the "eth_sendRawTransaction" endpoint
  let gethProxy = await fetch(`https://api-ropsten.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=${rawTransaction}&apikey=YourApiKeyToken`);    
  let response = await gethProxy.json();    
     
  // print the API response
  console.log(response);
  
}

main();

Wrapping Up

Last updated