Build more with QuickNode - New pricing plans and a free tier! Read the press release

How to Build a Solana Explorer Clone (Part 1: Transaction History)

August 08, 2022

Overview

Solana Explorer provides access to a wealth of great information on the Solana blockchain: transaction history and details, token balances, NFT metadata, and much more. In our opinion, browsing Solana Explorer is one of the best ways to understand Solana, but building a Solana Explorer takes your knowledge to the next level! In this 3 part series, we'll cover the steps needed to build a simple clone of Solana Explorer!

In our first guide, we're going to cover transactions. If you're building a dApp, chances are you'll want to share transaction history with your users. Some possible use cases can include: 
  • an NFT platform may want to show a user a history of an NFT's sales
  • a DAO may want to show a history of voting results
  • an exchange may want to show a user's buy/sell history
  • a non-profit may want to track donation logs

What You Will Do

In this guide, you will use the Solana Scaffold and Wallet Adapter to create a Solana Explorer framework. You'll use that framework to build a simple web app that will fetch transaction history for a wallet and display that history on your site. 

Sample Output: Transaction Log


What You Will Need


To follow along with this guide, you will need to have completed our guide on How to Connect Users to Your dApp with the Solana Wallet Adapter and Scaffold. Why? We will be building on top of the code that is there.

Before getting started, make sure that you have followed the instructions in that guide and completed these steps:

  1. Installed and launched the Solana Scaffold from your project directory.
  2. Updated your RPC endpoint.
  3. Created template.tsx in ./src/components/ (we'll be using this as our starting point).

The final code from the How to Connect Users to Your dApp with the Solana Wallet Adapter and Scaffold guide will serve as a starting point to this one. You should have the final code from this Github repo in a local code editor and your localhost:3000 should render the following view:
Current Local Host


Set Up Your Environment

Let's start by upgrading our scaffold to the latest version of Solana Web 3. We will be using yarn to initialize our project and install the necessary packages. Feel free to use npm instead if that’s your preferred package manager:

set up your environment

Copy
npm update @solana/web3.js
## or
yarn upgrade @solana/web3.js

Create a Transaction Log Component

From your project directory, duplicate your Component Template and name it TransactionsLog.tsx:

create a transaction log component

Copy
cp ./src/components/template.tsx ./src/components/TransactionsLog.tsx

TransactionsLog.tsx should now exist in your components folder. Open it in a code editor of choice. 

Update Dependencies


Start by renaming your Function Component to TransactionLog. Replace:

create a transaction log component

Copy
export const Template: FC = () => {
with 

create a transaction log component

Copy
export const TransactionLog: FC = () => {

For this component, we'll need additional imports. Replace the existing imports of your template with these: 

create a transaction log component

Copy
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { PublicKey, ParsedTransactionWithMeta } from '@solana/web3.js';
import { FC, useEffect, useState } from 'react';
import { notify } from "../utils/notifications";

Create State Variables


Inside of your TransactionLog Function Component, declare two state variables using useState
  1. transactionHistory - this is where we'll store the parsed transaction results of our Solana query 
  2. transactionTable - this is where we'll store a JSX Element that contains the output table that we'll display in our web app

We'll set both values to null to start:

create a transaction log component

Copy
    const { connection } = useConnection();
    const { publicKey } = useWallet();
    const [transactionHistory, setTransactionHistory] = useState<ParsedTransactionWithMeta[]>(null);
    const [transactionTable, setTransactionTable] = useState<JSX.Element>(null);

Create Solana Query 


Next, inside your TransactionLog Function Component create getTransactions, a function that will fetch parsed transactions for a given wallet and set that array of transaction data to the transactionHistory variable:

create a transaction log component

Copy
     async function getTransactions(address, numTx) {
        const pubKey = new PublicKey(address);
        //Find recent transactions
        let transactionList = await connection.getSignaturesForAddress(pubKey, {limit:numTx});
        //Parse transactions to get signature for recent transactions 
        let signatureList = transactionList.map(transaction=>transaction.signature);
        //Get parsed details of each transaction 
        let transactionDetails = await connection.getParsedTransactions(signatureList);
        //Update State
        setTransactionHistory(transactionDetails);
    }

For more information on fetching transactions logs on Solana visit our Guide: How to Get Transaction Logs on Solana.

Next, create a function that takes our Parsed transaction data and assembles it into a nice table. Here's the function we used which includes some custom styling and formatting, but feel free to modify it to include information that's important to you.  

Construct UI


Add the following buildTransactionTable function after getTransactions:

create a transaction log component

Copy
    function buildTransactionTable() {
        if(transactionHistory && transactionHistory.length !== 0) {
            let header = (
                <thead className="text-xs text-gray-700 uppercase bg-zinc-50 dark:bg-gray-700 dark:text-gray-400">
                    <tr>
                        <th className="px-6 py-3">Transaction Signature</th>
                        <th className="px-6 py-3">Slot</th>
                        <th className="px-6 py-3">Date</th>
                        <th className="px-6 py-3">Result</th>
                    </tr>
                </thead>
                )
            let rows = transactionHistory.map((transaction, i)=>{
                let date = new Date(transaction.blockTime*1000).toLocaleDateString();
                return (
                    <tr  key={i+1} className="bg-white border-b bg-zinc-800 dark:border-zinc-700">
                        <td className="px-6 py-3">
                                {/* some transactions return more than 1 signature -- we only want the 1st one */} 
                                {transaction.transaction.signatures[0]}
                        </td>
                        <td className="px-6 py-3">{transaction.slot.toLocaleString("en-US")}</td>
                        <td className="px-6 py-3">{date}</td>
                        <td className="px-6 py-3">{transaction.meta.err ? 'Failed' : 'Success'}</td>
                    </tr>)
            })
            setTransactionTable(
                <table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
                    {header}
                    <tbody>{rows}</tbody>
                </table>)
        } 
        else {
            setTransactionTable(null);
        }
    }

A couple notes about this function:
  • We are first checking to make sure a transactionHistory has been found and that we've found at least 1 entry.
  • Header defines the fields that we'll be searching for.
  • Rows will create a table row <tr> for each transaction in transactionHistory. Inside, we create a <td> element for each piece of data we will display.
  • We set our state variable, transactionTable as a <table> that includes our header and rows.

Trigger Query and Table Rendering


Now we need to create a React hook, useEffect to update call buildTransactionTable when our app has found Transactions. Add it below your state variable declarations and above your getTransactions function:

create a transaction log component

Copy
    useEffect(() => {
        if (publicKey && transactionHistory) {
            buildTransactionTable();
        }
      }, [publicKey, connection, transactionHistory])

Effectively this will look for a change in publicKey, connection, or transactionHistory and refresh our table based on the new parameters.

Let's create an onClick function after the buildTransactionTable function to initiate our search: 

create a transaction log component

Copy
    const onClick = async () => {
        if (!publicKey) {
            console.log('error', 'Wallet not connected!');
            notify({ type: 'error', message: 'error', description: 'Wallet not connected!' });
            return;
        }
        try { 
            await getTransactions(publicKey.toString(),15);
        } catch (error: any) {
            notify({ type: 'error', message: `Couldn't Find Transactions!`, description: error?.message });
            console.log('error', `Error finding Transactions! ${error?.message}`);
        }
    };

Our function first checks that we have a connected publicKey and performs a simple try/catch to to attempt to search for that public key using our getTransactions function. We're searching for 15 transactions in our example, but you can set the search value to however many transactions you'd like to return.

Finally, update your return statement to include a button that initiates onClick and a <div> that renders our table: 

create a transaction log component

Copy
    return(<div>
        <div className="text-center">
        <button
                className="px-8 m-2 btn animate-pulse bg-gradient-to-r from-[#9945FF] to-[#14F195] center hover:from-pink-500 hover:to-yellow-500 ..."
                onClick={onClick}
            >
                <span>Get Transactions</span>
        </button>
        </div>

    {/* Render Results Here */}
        <div>{transactionTable}</div>
    </div>)

If you're following along, your final file should look like this.

Create an Explorer View and Page

Great job! But before we go see our work in action, we need to tell our app where to display it. We'll need to do a couple of things to make our component visible in Next.js
  • Create an Explorer View
  • Create an Explorer Page

Create an Explorer View for our Solana Explorer Clone


From your project directory, create a new folder, explorer, and in it, create a new file, index.tsx:

create an explorer view and page

Copy
mkdir ./src/views/explorer
echo > ./src/views/explorer/index.tsx

Open ./src/views/explorer/index.tsx and paste this code: 

create an explorer view and page

Copy
import { FC, useEffect } from "react";
import { useWallet, useConnection } from '@solana/wallet-adapter-react';
import useUserSOLBalanceStore from '../../stores/useUserSOLBalanceStore';
import { TransactionLog } from "../../components/TransactionsLog";
{/* import { GetTokens } from "../../components/GetTokens"; */}

export const ExplorerView: FC = ({ }) => {
  const wallet = useWallet();
  const { connection } = useConnection();

  const balance = useUserSOLBalanceStore((s) => s.balance)
  const { getUserSOLBalance } = useUserSOLBalanceStore()

  useEffect(() => {
    if (wallet.publicKey) {
      console.log(wallet.publicKey.toBase58())
      getUserSOLBalance(wallet.publicKey, connection)
    }
  }, [wallet.publicKey, connection, getUserSOLBalance])

  return (
<div className="md:hero mx-auto p-4">
      <div className="md:hero-content flex flex-col">
        <h1 className="text-center text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-tr from-[#9945FF] to-[#14F195]">
          Quick View Explorer
        </h1>
        <div className="text-center">
          {wallet && wallet.publicKey && <p>Connected to: {(wallet.publicKey.toString())}</p>} 
          {wallet && <p>SOL Balance: {(balance || 0).toLocaleString()}</p>}
        </div>
        <div className="text-center">          
          <TransactionLog/>
          {/* <GetTokens/> */}
        </div>
      </div>
    </div>
  );
};

For this view, we're actually borrowing quite a bit of code from ./src/views/home/index.tsx which fetches a user's wallet balance on Wallet Connect. Below that, we call <TransactionLog/> to handle and display our transaction search. 
If you went through our previous guide to Create a Get Token Accounts Component, you could add that here too: just add import { GetTokens } from "components/GetTokens"; to your imports and  including <GetTokens/> after <TransactionLog/>.

The Solana Scaffold exports all views from a common file, ./src/views/index.tsx. Open it, and add this line to export ExplorerView

create an explorer view and page

Copy
export { ExplorerView } from "./explorer";

Create an Explorer Page that Publishes our View


From your project directory, create a new page, explorer.tsx:

create an explorer view and page

Copy
echo > ./src/pages/explorer.tsx

Paste this code into explorer.tsx

create an explorer view and page

Copy
import type { NextPage } from "next";
import Head from "next/head";
import { ExplorerView } from "../views";

const Explorer: NextPage = (props) => {
  return (
    <div>
      <Head>
        <title>Solana Scaffold</title>
        <meta
          name="description"
          content="Basic Functionality"
        />
      </Head>
      <ExplorerView />
    </div>
  );
};

export default Explorer;

This is the actual page that will render, /explorer/.

Alright! You've made it! Let's recap what we've done just to make sure we're all synced up. Here's a summary of new files you should have created: 

  • ./src/components/TransactionsLog.tsx  - new component that queries solana and renders a table on succesful result
  • ./src/views/explorer/index.tsx - new view that will host our transaction log (and other components in the future)
  • ./src/pages/explorer.tsx - new page to display our view ./explorer/

Let's see it in action! Enter:

create an explorer view and page

Copy
npm run dev
## or
yarn dev

And head over to http://localhost:3000/explorer. Do you see something like this? 

Checkpoint 1: Transaction Log


Pretty cool, right? Yes, great job! 

Conclusion

You just built a dApp that will query a user's wallet, find their transaction history, and renders that history on a dApp. Pretty powerful, huh? But what if we want to know more about a given transaction? We'll be covering that in our next guide in this series.

Ready to take the next step? Check out our Guide: Solana Explorer Clone Part 2 - Transaction Detail

We ❤️ Feedback! 

We're going to be releasing a few follow-up guides to build out this Solana Explorer Clone further. If you want to be the first to know about those or have any feedback or questions on this guide, please let us know. Feel free to reach out to us via Twitter or our Discord community server. We’d love to hear from you! 

Related articles 60

How to Send a Transaction On Solana Using JavaScript
Apr 13, 2022

Hello reader! Today is an exhilarating day because we are going on an expedition to the Solana Blockchain. Solana is an up-and-coming blockchain seeking to improve upon the current ecosystem's solutions to the complex problem of providing a secure, scalable, decentralized...

Continue reading
How to do a non-custodial transaction with QuickNode
Apr 12, 2022

Private keys are one of the most sensitive pieces of data when it comes to cryptography and the blockchain. However, there has always been debate/confusion about choosing between custodial wallets (where the wallet provider has custody of the user’s private key) and...

Continue reading
How to connect to Ethereum using .NET (Nethereum)
Apr 12, 2022

Dotnet or .NET is very popular for the development of desktop applications, most Windows desktop applications are built using .NET, and it also contributes largely to web application’s tech stack. In this guide, let’s see how we can connect to Ethereum using .NET and

Continue reading
How to Set Up a Near Project from Scratch
Jan 27, 2022

In this tutorial we will look at how we can setup a basic NEAR project from scratch, installing and configuring dependencies and customizing the project to work well with AssemblyScript.We will first start by initializing our project with a package.json file using...

Continue reading
Como crear y lanzar un ERC-721 (NFT)
Dec 29, 2021

Coleccionables digitales que son compatibles con ERC-721 se han vuelto muy populares desde el lanzamiento de Cryptokitties y han ganado adopción masiva en los últimos meses. Esta guía cubrirá la parte de creación y lanzamiento...

Continue reading
How to connect to Ethereum network using Java / Web3j
Apr 12, 2022

We can say that Java is one of the most versatile languages out there, and it continues to be relevant in today's time. Java is so popular because of its massive user base and use cases. In this guide/tutorial, we'll learn how to connect to the Ethereum Blockchain network...

Continue reading
How to integrate IPFS with Ethereum
Apr 12, 2022

It can be costly to store massive files on a blockchain mainnet, and this is where decentralized file storing systems like IPFS can come in handy. Sometimes, NFTs use IPFS as well. In this guide, we’ll cover how we can integrate IPFS with...

Continue reading
How to Connect to the Ethereum Network using Ruby
Jun 13, 2022

The Ruby programming language has a huge fanbase. Ruby was developed by its creator with an intention to invent a language developers can enjoy learning and using. Ruby has been largely accepted by developers all around the world since its launch, in fact, the biggest...

Continue reading
How to connect to Ethereum network with ethers.js
Apr 12, 2022

When someone thinks of developing a dApp the first tool that comes to their mind is web3.js which is pretty common because of its popularity in the community and wide use cases, dApp development has been consistently growing and there are a lot of developers who want to...

Continue reading
How to Mint an NFT on Solana
Apr 12, 2022

Updated at: April 10, 2022Welcome to another QuickNode guide on Solana - the up-and-coming blockchain that seeks to solve the scalability issues of Ethereum. We will be walking through step-by-step how to create an NFT on Solana. NFT, short for Non Fungible Token,...

Continue reading
The Web3 Developer Stack
Apr 12, 2022

A developer stack is a bag of technologies a developer possesses. For example, MEAN (MongoDB, Express.js, AngularJS/Angular, and Node.js) and MERN (MongoDB, Express.js, React, and Node.js) are common web developer stacks. Similarly, today we will learn more about the web3...

Continue reading
How to Get All Tokens Held by a Wallet in Solana
Jul 17, 2022

Hello readers! To kick off Solana Summer and the current whitelist meta, we thought it would be helpful to dig into all of the token accounts you and your users have using the getParsedProgramAccounts method. This tool is convenient for querying different...

Continue reading
How to deploy a smart contract with Brownie
Apr 12, 2022

Python is one of the most versatile programming languages; from researchers running their test models to developers using it in heavy production environments, it has use cases in every possible technical field. In today's guide, we will learn about Brownie, a Python-based...

Continue reading
Introduction to Scaffold-ETH 🏗
Dec 29, 2021

Developing applications involves juggling several moving pieces like front-ends, back-ends, and databases. But developing a decentralized application on a blockchain adds a few more elements like smart contracts and nodes that allow you to connect to the...

Continue reading
Como crear un NFT en SOLANA
Dec 29, 2021

¡Hola querido lector! Bienvenidos a una nueva guía de Solana.Solana es una blockchain que promete mucho a la hora de intentar resolver los problemas de escalabilidad que podemos apreciar en otras blockchains, como Ethereum por...

Continue reading
How to Send an EIP-1559 Transaction
Apr 12, 2022

While Ethereum has been trying to scale, it has encountered some gas price issues. Many layer 2 solutions and sidechains sprang into existence to solve this problem, but Ethereum is the main chain, and at some point, it has to be improved. EIP-1559 was introduced to...

Continue reading
How to Create an Address in Solana using JavaScript
Apr 12, 2022

Hello reader! Welcome to QuickNode's first Solana guide. Solana is an up-and-coming blockchain that seeks to solve the scalability issues that Ethereum has been handling. You will walk through step-by-step how to create a Solana address using the @solana/web3.js...

Continue reading
How to create your own DAO with Aragon
Apr 12, 2022

Blockchain provides us with the power of decentralization. Decentralization means the transfer of power to users/members rather than having a single centralized authority governing everything; it enables various use cases in finance, governance, voting, fundraising, etc....

Continue reading
How to Connect to Terra with JavaScript using Terra.js
Apr 12, 2022

Stablecoins have been bridging the gap between traditional currencies and blockchains. Stablecoins offer stable price tokens pegged by a reserve asset which is often a fiat current like USD, EUR, GBP. The Terra protocol provides a framework to work with stablecoins. This...

Continue reading
How to connect to Ethereum network using Go
Apr 12, 2022

Go helps you make faster scalable backends and this guide will show you how to connect your backend to Ethereum (and make it even faster, more reliable, and globally accessible, all thanks to QuickNode’s global infrastructure). What is...

Continue reading
How To Fork Ethereum Mainnet with Hardhat
Apr 12, 2022

Forking the chain at an older block of the blockchain is helpful if you want to simulate the blockchain’s state at that block; Hardhat has this functionality built in. In this guide, let’s go through the process of forking the Ethereum Mainnet at an older...

Continue reading
How to connect to Ethereum using PHP
Apr 12, 2022

PHP is a very popular choice among developers and has a vast community due to its long presence in web development. In this guide, we’ll cover how to connect to Ethereum with PHP using the web3.php...

Continue reading
How to use Subspace with QuickNode
Apr 12, 2022

In this guide, we'll understand a bit about reactive development and how to use Subspace with QuickNode.JavaScript is the programming language behind most of the internet apps and websites. JavaScript today has become one of the most used programming languages,...

Continue reading
How to Connect Your Dapp With MetaMask Using Ethers.js
Dec 29, 2021

In our dApp, we will have a simple react user interface that has a material button asking the user to connect to MetaMask. And if they do not have an account, they can create one or log in to their account. They will then view their wallet balance and address displayed on...

Continue reading
How to generate a new Ethereum address in Go
Dec 29, 2021

Golang is very popular among backend developers for building infrastructures and microservices. Go is a procedural programming language. Developed in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson at Google, then launched in 2009 as...

Continue reading
How to generate a new Ethereum address in Python
Dec 29, 2021

Python is one of the most versatile programming languages out there with an abundance of use cases; We can build many applications with Python from client-side to back end. In this guide, we will cover creating an Ethereum address in Python using the

Continue reading
How to Get Transaction Logs on Solana
Jul 11, 2022

Ever need to pull all the transactions associated with a Wallet? Want to see all of the mint transactions associated with a Candy Machine? Or maybe see transaction history of an NFT? Solana's getSignaturesForAddress method is a versatile tool that makes...

Continue reading
How to Lazy Mint an NFT on Rarible with Rarepress
Apr 12, 2022

NFTs are great for creators to monetize their artwork and for people to get ownership of an item. But since gas prices are usually high given the highly in-demand space on Ethereum, minting an NFT or an NFT collection can become costly for a creator. Lazy minting solves...

Continue reading