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 benull
when the transaction is still pending.
meta.err
will contain the error if the transaction failed. It the transaction wass successful, it will benull
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 themeta.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 themeta.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
ormeta.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
andmeta.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:
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:
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