Solana - SPL Initialized Account
SPL Initialized Account Foundational Store
A specialized foundational store for tracking SPL token account initializations on Solana. This store provides the essential account-to-owner mappings needed to resolve SPL token transfers, since transfer instructions only contain account addresses without owner information.
Overview
The SPL Initialized Account foundational store provides efficient storage and retrieval of:
Account-Owner Mappings: Relationship between SPL token accounts and their owners
Mint Associations: Which token mint each account is associated with
Initialization Events: Tracking of newly created SPL token accounts
SPL token transfer instructions on Solana only contain account addresses, not the wallet owners. To determine who actually sent/received tokens, you need to resolve account ownership. This foundational store provides that critical mapping.
Consuming Account Owner Data
use substreams::store::FoundationalStore;
use std::collections::HashSet;
#[substreams::handlers::map]
fn map_spl_instructions(
params: String,
transactions: SolanaTransactions,
foundational_store: FoundationalStore,
) -> Result<SplInstructions, Error> {
// ... extract transfer instructions from transactions
// Collect account addresses that need owner lookup
let mut accounts_to_lookup = HashSet::<String>::new();
accounts_to_lookup.insert(transfer.from.clone());
accounts_to_lookup.insert(transfer.to.clone());
// Convert addresses to bytes and query store
let account_bytes: Vec<Vec<u8>> = accounts_to_lookup
.iter()
.filter_map(|addr| bs58::decode(addr).into_vec().ok())
.collect();
let resp = foundational_store.get(&account_bytes);
// Process responses and decode owner data
for queried_entry in resp.entries {
if queried_entry.code != ResponseCode::Found as i32 {
continue;
}
let Some(entry) = &queried_entry.entry else { continue; };
let Some(value) = &entry.value else { continue; };
let Ok(account_owner) = AccountOwner::decode(value.value.as_slice()) else {
continue;
};
// Use owner data to enrich transfers
let owner_b58 = bs58::encode(&account_owner.owner).into_string();
transfer.from_owner = owner_b58;
}
Ok(SplInstructions { instructions })
}Consumer Module (uses foundational store as input):
Complete Example: See the map_spl_instructions implementation.
Data Model
Key Structure
The store uses SPL token account addresses as keys:
Value Schema
AccountOwner (type.googleapis.com/sf.substreams.solana.spl.v1.AccountOwner)
Implementation details
The foundational store processes SPL token account initialization instructions to extract account-to-owner mappings. It tracks three instruction types:
InitializeAccount- Basic account initialization with separate owner accountInitializeAccount2- Account initialization with embedded owner in instruction dataInitializeAccount3- Newer variant of account initialization with embedded owner
Each SPL token account address becomes a key, with the corresponding AccountOwner protobuf message containing mint and owner information as the value.
Implementation
Creating Foundational Store Entries
Substreams Manifest Configuration
Producer Module (creates foundational store entries):
Complete Example: See the map_spl_initialized_account implementation.
Related Resources
Foundational Stores Architecture
Last updated
Was this helpful?

