This Substreams module tracks SPL token account initializations on Solana, extracting account-to-owner mappings. This is essential for resolving SPL token transfers since the transfer instructions don't contain owner information directly.
If you are a Substreams developer, jump to Query the Solana SPL Initialized Account Foundation Store for details about how to resolve SPL token ownership when processing SPL Token instructions.
This module is typically used by SPL token transfer modules to resolve account ownership. Since SPL transfer instructions only contain account addresses, you need this data to determine who actually sent/received the tokens.
Example usage pattern:
[!NOTE] Ensure your Cargo
substreams
andsubstreams-solana
dependencies are up to datecargo update substreams substreams-solana
.
First, add you substreams.yaml
manifest the SPL Initialized Account import and depend on the Solana SPL Initialized Account Foundation Store:
...
imports:
...
spl_initialized_account: spl-initialized-account@v0.1.2
modules:
...
- name: <your-module>
kind: map
inputs:
...
- foundational-store: spl-initialized-account@v0.1.2
output:
...
Then in your code, adjust your modules Rust input to align with its new definition:
...
use substreams::store::FoundationalStore;
#[substreams::handlers::map]
fn map_spl_instructions(..., spl_initialized_account_store: FoundationalStore) -> Result<SplInstructions, Error> {
...
// Resolve one owner
let response = foundational_store.get(<account_address_bytes>);
if response.response == ResponseCode::Found as i32 {
if let Ok(account_owner) = AccountOwner::decode(response.value.unwrap().value.as_slice()) {
substreams::log::info!("Owner is {}", Address(&account_owner.owner).to_string());
}
}
// Resolve batch of owners
// It's highly recommend to make a single get_all by block to resolve your owner, this will
// greatly improve the performance of your Substreams
let response = foundational_store.get_all(<vec of account_address_bytes>);
for entry in response.entries {
// Same handling as single case above
}
}
See https://github.com/streamingfast/substreams-spl-token for a full example of SPL Initialized Account Foundational Store usage.
The module processes Solana transactions and extracts information about newly initialized SPL token accounts from these instruction types:
InitializeAccount
- Basic account initializationInitializeAccount2
- Account initialization with embedded ownerInitializeAccount3
- Account initialization with embedded owner (newer variant)For each initialized account, it stores the relationship between:
The module is configured to process transactions from both SPL Token programs:
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
(original)TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
(Token-2022)substreams gui spl-initialized-account@v0.1.2 map_spl_initialized_account
substreams gui spl-initialized-account@v0.1.2 solana_common:blocks_without_votes
substreams gui spl-initialized-account@v0.1.2 solana_common:transactions_by_programid_without_votes
blocks_without_votes
allows you to consume a full Solana Block without Vote instructions (Vote111111111111111111111111111111111111111
).
If you consume it on HISTORICAL data (+1000 blocks from HEAD), you will be reading from the StreamingFast cache, thus saving costs on the amount of TB read.
substreams gui spl-initialized-account@v0.1.2 solana_common:v020:blocks_without_votes
substreams gui spl-initialized-account@v0.1.2 solana_common:program_ids_without_votes