Introduction
When using our Solana API endpoints, it's important to understand how Solana token accounts work and how this affects fetching and parsing an account's transaction history.
In Solana, an account can own multiple sub-accounts (referred to as "token accounts" from here on), each associated with a particular token held by the main account.
To obtain a complete transaction history for a Solana account, you need to consider not only the main account but also all its associated token accounts, both active and closed. This guide explains how to achieve this using our API endpoints.
Understanding Token Accounts
The /splAccounts
endpoint returns all active token accounts owned by the main account you are querying. However, due to limitations in Solana's underlying RPC infrastructure, it does not return token accounts that have been closed by the time of your query. This means that if your main account previously held tokens that it no longer holds, those closed token accounts will not be listed.
Fetching a Complete Transaction History
To fetch a fully complete list of transactions for a given account (including all associated token accounts, current and past), you need to follow these steps:
1. Retrieve Active Token Accounts
Call the /splAccounts
endpoint with the main account address. This will return a list of all active associated token account addresses.
2. Initialize the Accounts List
Create a set that includes the main account address and all the associated token account addresses obtained from the previous step.
3. Iteratively Fetch Transactions
For each account in the set:
- a. Call the
/txs
endpoint for that account and retrieve all transactions. - b. Keep a running tally of all transactions returned (you'll probably need to de-duplicate transactions later).
- c. In each transaction, inspect the
transfers
array. For each transfer, examine theowner
field of thefrom
andto
addresses. - d. If the
owner
address is one of the accounts in your set, add this new address to your set of accounts to fetch transactions for.
Example:
{
"from": {
"name": null,
"address": "Gwk6eDbPWpw6YEfDvQ9KodGWghWnGdVMbkEdvWxRBtd",
"owner": {
"name": null,
"address": "2VoJ7iR1KSJ9dUTsN6Mt1EEVhbP13Zjk2EfVpJXTcLRN"
}
}
In this example, if the owner
address (2VoJ7...
) is one of the addresses in your current set, you should add the associated address (Gwk6...
) to your set of accounts to fetch transactions for.
4. Repeat the Process
Continue this process recursively until no new associated accounts are found. Once you've called txs
for every account in the running tally, and have de-duped the list, you'll effectively have all relevant transactions in the history of your main account.
This will give you a result that reconciles, for accounting purposes.
Sample Code
Here's a sample Python script that demonstrates how to implement this logic:
import json
import requests
def get_spl_accounts(main_account, api_key):
url = f"https://translate.noves.fi/svm/solana/splAccounts/{main_account}"
headers = {"apiKey": api_key}
response = requests.get(url, headers=headers)
return response.json().get("accounts", [])
def get_transactions(account, api_key):
url = f"https://translate.noves.fi/svm/solana/txs/{account}"
headers = {"apiKey": api_key}
transactions = []
next_url = url
while next_url:
response = requests.get(next_url, headers=headers)
response.raise_for_status()
data = response.json()
transactions.extend(data.get("items", []))
next_url = data.get("nextPageUrl", "")
return transactions
def find_associated_accounts(transactions, known_accounts):
new_accounts = set()
for tx in transactions:
for transfer in tx.get("transfers", []):
from_info = transfer.get("from", {})
from_address = from_info.get("address")
from_owner = from_info.get("owner", {}).get("address")
to_info = transfer.get("to", {})
to_address = to_info.get("address")
to_owner = to_info.get("owner", {}).get("address")
for address in [from_address, to_address, from_owner, to_owner]:
if (
address
and address not in known_accounts
and any(known in [from_address, to_address, from_owner, to_owner] for known in known_accounts)
):
new_accounts.add(address)
return new_accounts
def fetch_complete_history(main_account, api_key):
known_accounts = set(get_spl_accounts(main_account, api_key))
known_accounts.add(main_account)
all_transactions = []
accounts_to_process = known_accounts.copy()
while accounts_to_process:
account = accounts_to_process.pop()
transactions = get_transactions(account, api_key)
all_transactions.extend(transactions)
new_accounts = find_associated_accounts(transactions, known_accounts)
accounts_to_process.update(new_accounts - known_accounts)
known_accounts.update(new_accounts)
seen_tx_ids = set()
unique_transactions = []
for tx in all_transactions:
tx_id = tx.get("rawTransactionData", {}).get("signature")
if tx_id and tx_id not in seen_tx_ids:
seen_tx_ids.add(tx_id)
unique_transactions.append(tx)
return unique_transactions
if __name__ == "__main__":
api_key = "" # ENTER API KEY HERE
main_account = "" # ENTER MAIN SOLANA ACCOUNT HERE
transactions = fetch_complete_history(main_account, api_key)
with open("transactions.json", "w") as f:
json.dump(transactions, f)