SPL Token Tracker
Last updated
Was this helpful?
Last updated
Was this helpful?
The Solana Token Tracker Substreams allows you to extract transfers from Solana Token Programs. You can simply provide the address of the token you want to track as an input to the Substreams.
The Solana Token Tracker Substreams requires medium to advanced Substreams knowledge. If this is the first time you are using Substreams, make sure you:
Read the section, which will teach you the basics of the developing Substreams modules.
Complete the tutorial, which will assist you in understanding the main pieces of the Solana Substreams.
If you already have the required knowledge, clone the . You will go through the code in the following steps.
The Substreams has only one module: map_solana_token_events
, as you can check in the Substreams manifest (substreams.yaml
):
The module receives two inputs (defined in the inputs
section of the YAML):
A string containing a couple of parameters: this parameter is defined in the params
section of the YAML, and defines the token that you want to extract data from: token_contract=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&token_decimals=6
token_contract=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
is the address of the USDC contract in Solana mainnet and token_decimals=6
is the number of decimals used the USDC token.
A raw Solana block.
You can update the token_contract
parameter to track any token of your choice. You can also use the -p
option in the Substreams GUI to dynamically override the parameters of the Substreams.
You can run the Substreams by using the Substreams CLI. As specified in the manifest by default, the USDC data will be retrieved.
You can also override the parameters of the manifest by using the -p
option of the CLI. For example, if you want to track the transfer of the USDT token (Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
):
Open the lib.rs
file, which contains the code for the map_solana_token_events
module. The function receives two parameters: the raw Solana block object and the parameters provided in the Substreams manifest.
The parse_parameters
function converts the parameters string passed to the module and converts it into a TokenParams
object. This object contains two fields: token_contract
(representing the address of the token to track) and token_decimals
(representing the number of decimals used in the token).
Then, you iterate over all the transactions
Create an Output
object, which is the container of all the events extracted.
Iterate over the confirmed transactions of the block.
Unwrap the transaction if it is available.
Unwrap the transaction message.
Unwrap the transaction metadata.
Iterate over the instructions contained within the transaction.
For every instruction, call the process_compiled_instruction(...)
function to process the instruction further.
The process_compiled_instruction(...)
function is defined in the util.rs
file.
The instruction.program_id_index
indicates the position of the program account in the accounts array. For example, if program_index_id = 5
, it means that the program account will be at position number 5 in the accounts
array.
If the instruction account is the Token Program Account (i.e. TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
), this means that the instruction executed in the transaction has been produced by the Token Program. Therefore, you process the instruction further by calling the process_token_instruction(...)
function to extract token-related information, such as transfers or mints.
Every top-level instruction holds inner instructions. If the top-level instruction is not from the Token Program, you check if any Inner Instruction is from the Token Program by calling the process_inner_instructions(...)
function.
A top-level instruction could hold a Token Program instruction within its inner instructions. The process_inner_instructions(...)
checks if there are Token Program among the inner instructions of every top-level instruction.
The TransactionStatusMeta
object holds an array with the inner instructions of the transaction (an array of InnerTransactions
objects).
Because the inner instructions are at the transaction level (contained within the TransactionStatusMeta
), you keep only the inner transactions belonging to the current top-level instruction. For this purpose, an index variable (instruction_index
) is passed as a parameter. Essentially, you are matching every top-level instruction with its corresponding InnerTransactions
object. The filtering should only keep one InnerTransactions
object, as every top-level instruction should only have one InnerTransactions
object.
The InnerTransactions
object is a just wrapper for the array of inner transactions. For every InnerTransactions
object filtered (which should be only one), you actually extract the inner instructions.
You iterate over the array of inner instructions.
You only keep Token Program inner instructions.
You process every Token Program inner instruction found further by calling the process_token_instruction(...)
.
Once you have identified all the Token Program instructions, the process_token_instruction(...)
function extracts transfer or mint data from these instructions. To easily extract data from a Token Program instruction, the Substreams relies on the substreams-solana-program-instructions
Rust crate, which provides useful helper functions.
The TokenInstruction::unpack(...)
function decodes the instruction and allows you to identify the action executed: Transfer
, TransferChecked
, Mint
, or Burn
.
Controlled way to handle errors from the unpack(...)
function.
If there are no errors, then you can handle every action (Transfer
, Mint
...) differently.
Handle the Transfer
instruction.
Call the is_token_transfer(...)
to verify if the transfer is from the specified in the parameters of the Substreams module. Note that you pass parameters.token_contract
as a parameter to the function.
Create a new Transfer
object from the Protobuf with the corresponding data. This object is added to the Output
object and will be emitted as the output of the Substreams.
The code for other actions (Mint
, Burn
...) is analogous to the code of the Transfer
instructions.
Get the accounts of the transaction. The resolved_accounts()
method contains also accounts stored in the .