How to get the state of a transaction on Solana using JavaScript

In this tutorial, we are going to learn how to get information about the state of a Solana transaction using JavaScript, with the Solana Web3.js library and the JSON-RPC API.

In this tutorial, we are going to learn how to get information about the state of a Solana transaction using JavaScript, with the Solana Web3.js library and the JSON-RPC API.

How to get the state of a Solana transaction using the Solana Web3.js library

For that, we are going to use the getParsedTransaction function which takes in parameter the signature of the transaction to get and it returns a ParsedTransactionWithMeta object.

Here is an example:

import { Connection, clusterApiUrl } from '@solana/web3.js'

// you can also pass "mainnet-beta" or "testnet" here
const connection = new Connection(clusterApiUrl('devnet'))

const signature = "3A73BtgXVMZUVAvF1dLEwekuK55DH9GKpZbkyLuYxxseA3JijXr9x3EMU91jQCcLrSMh95GZHLkPVioQAqGMsANN"

const transaction = await connection.getParsedTransaction(signature)

If you're using React, you can use the useConnection hook to get the connection to the blockchain instead of creating a new one like in the example.

The most important properties in that object are:

  • blockTime the time when the transaction was included in a block.
    It will be null when the transaction is still pending.

  • meta.err will contain the error if the transaction failed. It the transaction wass successful, it will be null

  • meta.fee will tell you what was the network fee (it's in Lamports and not in SOL)

  • meta.postBalances will tell you, for each address involved in the transaction, what SOL balance it has after the transaction completed (in Lamports).
    In case you're sending custom tokens and not SOL, check the meta.postTokenBalances

  • meta.preBalances will tell you, for each address involved in the transaction, what SOL balance it had before the transaction completed (in Lamports). In case you're sending custom tokens and not SOL, check the meta.preTokenBalances

  • transaction.message.accountKeys is the list of addresses involved in the transaction. It has the same order as the balance properties (meta.postBalances, meta.preBalances, meta.preTokenBalances or meta.postTokenBalances). Having that same order will tell you what is the final balance of each address and what was the balance before the transaction which will also help you compute the amount of tokens sent. Don't mind the last one though, it is where the network fee went.

To sum up:

  • You can use the blockTime property to know if the transaction is pending or not
  • You can use the meta.err property to know if the transaction failed or not.
  • You can use meta.preBalances and meta.postBalances properties to know the amount of SOL that was sent in the transaction. You have the same properties for the token balances.

Check out the documentation to see all the properties available and an explanation about each of these properties:

JSON RPC API | Solana Docs
Solana nodes accept HTTP requests using the JSON-RPC 2.0 specification.

For the example above, the returned object is the following:

{
    "blockTime": 1668079848,
    "meta": {
        "err": null,
        "fee": 5000,
        "innerInstructions": [],
        "logMessages": [
            "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]",
            "Program log: Instruction: Transfer",
            "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4644 of 200000 compute units",
            "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success"
        ],
        "postBalances": [
            1896763840,
            2039280,
            2039280,
            934087680
        ],
        "postTokenBalances": [
            {
                "accountIndex": 1,
                "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
                "owner": "GX6nkQgcXy4xDyuSH9MKjG9nq5KN5ntE3ZUUHSqUrcC8",
                "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                "uiTokenAmount": {
                    "amount": "990000000",
                    "decimals": 6,
                    "uiAmount": 990,
                    "uiAmountString": "990"
                }
            },
            {
                "accountIndex": 2,
                "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
                "owner": "A8t59GvWSN6W3W4LKcqKNDhm9YYDEL8PSt235fyECA8J",
                "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                "uiTokenAmount": {
                    "amount": "10000000",
                    "decimals": 6,
                    "uiAmount": 10,
                    "uiAmountString": "10"
                }
            }
        ],
        "preBalances": [
            1896768840,
            2039280,
            2039280,
            934087680
        ],
        "preTokenBalances": [
            {
                "accountIndex": 1,
                "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
                "owner": "GX6nkQgcXy4xDyuSH9MKjG9nq5KN5ntE3ZUUHSqUrcC8",
                "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                "uiTokenAmount": {
                    "amount": "1000000000",
                    "decimals": 6,
                    "uiAmount": 1000,
                    "uiAmountString": "1000"
                }
            },
            {
                "accountIndex": 2,
                "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
                "owner": "A8t59GvWSN6W3W4LKcqKNDhm9YYDEL8PSt235fyECA8J",
                "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                "uiTokenAmount": {
                    "amount": "0",
                    "decimals": 6,
                    "uiAmount": null,
                    "uiAmountString": "0"
                }
            }
        ],
        "rewards": [],
        "status": {
            "Ok": null
        }
    },
    "slot": 174644546,
    "transaction": {
        "message": {
            "accountKeys": [
                {
                    "pubkey": "GX6nkQgcXy4xDyuSH9MKjG9nq5KN5ntE3ZUUHSqUrcC8",
                    "signer": true,
                    "source": "transaction",
                    "writable": true
                },
                {
                    "pubkey": "AqNjCUp2ZFf4oQvibNYhhVksN5Q7M1FGoB5kbwVJfnnN",
                    "signer": false,
                    "source": "transaction",
                    "writable": true
                },
                {
                    "pubkey": "JCHuwrirFLe2Wh1KbvrTXfh9SbyUE2XNRQGStEQow19L",
                    "signer": false,
                    "source": "transaction",
                    "writable": true
                },
                {
                    "pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                    "signer": false,
                    "source": "transaction",
                    "writable": false
                }
            ],
            "addressTableLookups": null,
            "instructions": [
                {
                    "parsed": {
                        "info": {
                            "amount": "10000000",
                            "authority": "GX6nkQgcXy4xDyuSH9MKjG9nq5KN5ntE3ZUUHSqUrcC8",
                            "destination": "JCHuwrirFLe2Wh1KbvrTXfh9SbyUE2XNRQGStEQow19L",
                            "source": "AqNjCUp2ZFf4oQvibNYhhVksN5Q7M1FGoB5kbwVJfnnN"
                        },
                        "type": "transfer"
                    },
                    "program": "spl-token",
                    "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
                }
            ],
            "recentBlockhash": "6f7rVwCHo6pkELHBM8y5DyVdP3QJoin6RQBB5vGuiMx4"
        },
        "signatures": [
            "3A73BtgXVMZUVAvF1dLEwekuK55DH9GKpZbkyLuYxxseA3JijXr9x3EMU91jQCcLrSMh95GZHLkPVioQAqGMsANN"
        ]
    }
}

How to get data about a transaction using the Solana JSON-RPC API

First, if you're using Node.js, you need to install this library to send API calls:

npm install node-fetch

And import it that way:

import fetch from 'node-fetch'

// OR

const fetch = require('node-fetch')

Then we need a URL to interact with the blockchain and send the API calls.

For that, we can use the clusterApiUrl like we've done above to create the Connection. We can use it to connect to the Solana Mainnet, Testnet or Devnet:

import { clusterApiUrl } from "@solana/web3.js"

// you can also pass 'devnet' or 'testnet'
const URL = clusterApiUrl("mainnet-beta");

Now that we have that URL, let's use it to call the Solana JSON-RPC API. We're going to call the getTransaction method:

const res = await fetch(URL, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'get-transaction',
      method: 'getTransaction',
      params: [signature],
    }),
});
const data = await res.json();

In this code, signature is the signature of the transaction you want to get information for. It is a unique string that is used to identify your transaction. It is also called transaction ID or transaction hash.

In the code above, we send an HTTP request to the Solana RPC API of your choice (mainnet in our case) and we call the getTransaction with the transaction signature as the only parameter.

Once we have the result, we convert it to JSON and then we're able to read the data from it.

Inside the result property, you'll have the same data as what the getParsedTransaction function returns.

Again, check out the documentation get more information about the data that a transaction has:

JSON RPC API | Solana Docs
Solana nodes accept HTTP requests using the JSON-RPC 2.0 specification.

Don't use the public URLs in production

In production, you should use URLs from a blockchain API provider instead of the public URLs that the clusterApiUrl function provides.

The public URLs have rate-limiting so if you use them in production, you'll send too many requests to it and at some point, it will start throwing errors.

Instead, you can use a blockchain API provider that supports the Solana blockchain like GetBlock, Alchemy or QuickNode.

These services manage nodes and when you send an API call to these URLs (either directly through the RPC API or using the Solana Web3.js library), they will forward it to the blockchain for you.

And that's it 🎉

Thank you for reading this article