How to parse Raw Transactions on Solana

This article illustrates how we can parse raw transactions on Solana written using the anchor framework using their respective IDL.

Khac Vy
Shyft.to

--

Do you ever wonder how Shyft Translator and other Solana explorers can parse your transactions and display them in a human-readable format? In this article, we will show you how we parse a transaction of a program written using the Anchor framework into a human-readable format.

To begin parsing transactions of an Anchor program, the first requirement is the IDL of this program. In Solana programming, the Interface Definition Language (IDL) specifies a program’s public interface, including the definition of the Solana program’s account structures, instructions, and error codes. IDLs are .json files used to generate client-side code, enabling users to interact with a Solana program conveniently.

In this article, we will demonstrate how to parse a transaction from the Metaplex Candy Machine V2. You can view the IDL for this program by clicking here. To locate the IDL of your program, execute the anchor build command to build your program. After the build process, the IDL file can be found at the following path: <your-program-root-folder>/target/idl/<your-program-name>.json

{
"version": "0.1.0",
"name": "demo_anchor",
"instructions": [
{
"name": "initialize",
"accounts": [
{
"name": "newAccount",
"isMut": true,
"isSigner": true
},
{
"name": "signer",
"isMut": true,
"isSigner": true
},
{
"name": "systemProgram",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "data",
"type": "u64"
}
]
}
],
"accounts": [
{
"name": "NewAccount",
"type": {
"kind": "struct",
"fields": [
{
"name": "data",
"type": "u64"
}
]
}
}
]
}

Once you have obtained the IDL file, you can begin parsing the transaction. The following code snippet demonstrates how to achieve this:

const { clusterApiUrl, Connection } = require('@solana/web3.js');
const fs = require('fs/promises');
const { BorshCoder } = require('@coral-xyz/anchor')

const connection = new Connection(
clusterApiUrl('mainnet-beta'), 'confirmed',
)

async function main() {
// load IDL file
const fileContent = await fs.readFile('candy-machine-v2.json', 'utf-8');
const borshCoder = new BorshCoder(JSON.parse(fileContent));

// sample signature
const txn = await connection.getTransaction('2zvo8i6VqcN9D9kZeqDJZCDC4FMak7HBfZXLGieFNhLi5aPWmoGMmnZtG14xPptdGtKP8uDemiSdmsxXC9WMz44Y');

// `4` is the index of the instruction which interacts with Candy Machine V2
const instruction = txn.transaction.message.instructions[4]
const decodedIx = borshCoder.instruction.decode(instruction.data, 'base58')
const accountMetas = instruction.accounts.map(
(idx) => ({
pubkey: txn.transaction.message.accountKeys[idx],
isSigner: txn.transaction.message.isAccountSigner(idx),
isWritable: txn.transaction.message.isAccountWritable(idx),
}),
);
const formatted = borshCoder.instruction.format(decodedIx, accountMetas);

console.log({ name: decodedIx.name, ...formatted });
}

main();

After executing the above code, the result will look like this. The name field represents the name of the function, the args field contains the list of arguments required for the function, and the accounts field lists the accounts needed for this function.

{
name: 'mintNft',
args: [ { name: 'creatorBump', type: 'u8', data: '254' } ],
accounts: [
{
name: 'Candy Machine',
pubkey: [PublicKey [PublicKey(AcR4Hi7hxnyJdXY4HkcL2P94QrNYdkr4NNnfPPFK15Pg)]],
isSigner: false,
isWritable: true
},
{
name: 'Candy Machine Creator',
pubkey: [PublicKey [PublicKey(5TK4BXYVUQrpgxKHNn89kExpGnZSjoFQZ9w172oDv7Dq)]],
isSigner: false,
isWritable: false
},
{
name: 'Payer',
pubkey: [PublicKey [PublicKey(CNLE75xYtJXqiaYovC5gb7BGrKAzqXTLKNgdPskqTAtr)]],
isSigner: true,
isWritable: true
},
{
name: 'Wallet',
pubkey: [PublicKey [PublicKey(RTieWhhgy4j3Gr5FkUXX5MLpyEeaFJbquJ4xE8nsidE)]],
isSigner: false,
isWritable: true
},
{
name: 'Metadata',
pubkey: [PublicKey [PublicKey(EsW67JUMP8s5vRjNPVHGTJ5rHv9VvFxEkMzmRy91NS7o)]],
isSigner: false,
isWritable: true
},
{
name: 'Mint',
pubkey: [PublicKey [PublicKey(3nGmvv2zvm6JPe7THs56BZak3oUsqjYd7nvGypgBQZjH)]],
isSigner: true,
isWritable: true
},
{
name: 'Mint Authority',
pubkey: [PublicKey [PublicKey(CNLE75xYtJXqiaYovC5gb7BGrKAzqXTLKNgdPskqTAtr)]],
isSigner: true,
isWritable: true
},
{
name: 'Update Authority',
pubkey: [PublicKey [PublicKey(CNLE75xYtJXqiaYovC5gb7BGrKAzqXTLKNgdPskqTAtr)]],
isSigner: true,
isWritable: true
},
{
name: 'Master Edition',
pubkey: [PublicKey [PublicKey(9B3Q5ddBP7EZ6PAAmxSgf1ZoPQrjXhVsXDjYs79ZynFJ)]],
isSigner: false,
isWritable: true
},
{
name: 'Token Metadata Program',
pubkey: [PublicKey [PublicKey(metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s)]],
isSigner: false,
isWritable: false
},
{
name: 'Token Program',
pubkey: [PublicKey [PublicKey(TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA)]],
isSigner: false,
isWritable: false
},
{
name: 'System Program',
pubkey: [PublicKey [PublicKey(11111111111111111111111111111111)]],
isSigner: false,
isWritable: false
},
{
name: 'Rent',
pubkey: [PublicKey [PublicKey(SysvarRent111111111111111111111111111111111)]],
isSigner: false,
isWritable: false
},
{
name: 'Clock',
pubkey: [PublicKey [PublicKey(SysvarC1ock11111111111111111111111111111111)]],
isSigner: false,
isWritable: false
},
{
name: 'Recent Blockhashes',
pubkey: [PublicKey [PublicKey(SysvarS1otHashes111111111111111111111111111)]],
isSigner: false,
isWritable: false
},
{
name: 'Instruction Sysvar Account',
pubkey: [PublicKey [PublicKey(Sysvar1nstructions1111111111111111111111111)]],
isSigner: false,
isWritable: false
}
]
}

It’s worth mentioning that in this example, we parse a transaction that interacts with multiple programs, such as the System program, Candy Machine program v2, and Token Metadata program. Therefore, if you intend to parse all the instructions, you will require the IDL of all the programs involved. In our case, we only use Candy Machine program v2, parsing just the 4th instruction.

Wrap up

This article has just helped you learn how you can build a parsing pipeline by yourself. By using the IDL, developers can communicate with Solana programs and comprehend the format of their transactions. However, if you prefer not to manage all of these aspects on your own, you can utilize Shyft’s Transaction API, which handles the complex and dependable parsing for you.

If you enjoyed this blog, feel free to check out our other blogs on Working with Compressed NFTs on Solana, or Building a Discord Bot with Shyft API. You can also checkout our newly launched JavaScript SDK, with a lot of cool features and functions for various actions on Solana.

Resources

--

--