3

How to take a snapshot of all holders of a Token2022? I get an output of 0

require('dotenv').config();
const fs = require('fs');
const path = require('path');
const {PublicKey, Connection} = require('@solana/web3.js');
const {get} = require('lodash');

const connection = new Connection(process.env.RPC_HOST);

async function getTokenOwners(address, tokenDecimals) {
    // The value 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
    // is the public key for the Solana Token Program.This program is a part of the Solana blockchain's ecosystem,
    // and it manages all SPL(Solana Program Library) token operations, including minting, transferring, and burning tokens.
    const TOKEN_PUBKEY = new PublicKey(
        'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
    );

    const filters = [
        {
            memcmp: {
                offset: 0,
                bytes: address,
            },
        },
        {
            dataSize: 165,
        },
    ];
    const programAccountsConfig = {
        filters,
        encoding: 'jsonParsed',
    };
    const listOfTokens = await connection.getParsedProgramAccounts(
        TOKEN_PUBKEY,
        programAccountsConfig
    );

    return listOfTokens
        .map((token) => {
            const address = get(token, 'account.data.parsed.info.owner');
            const amountString = get(
                token,
                'account.data.parsed.info.tokenAmount.amount'
            );
            const amount =
                parseInt(amountString, 10) / Math.pow(10, tokenDecimals);

            return {
                address,
                amount,
            };
        })
        .sort((a, b) => b.amount - a.amount);
}

async function main() {
    console.log('Getting token holders');

    const tokenAddress = 'EVoLsbmQXT6R3b11WjPpGEXyCjw1zmmir271XqDbKRsg';
    const tokenDecimals = 9;

    try {
        const owners = await getTokenOwners(tokenAddress, tokenDecimals);

        const outputFile = 'tokens-owner.json';
        fs.writeFileSync(
            path.resolve(outputFile),
            JSON.stringify(owners, null, 2)
        );

    } catch (error) {
        console.log('Error: ', error.message || error);
    }
}

main();

1 Answer 1

2

The filters for token accounts are a bit off. With token-2022, token accounts can include optional "extensions", so their size is often going to be larger than 165 bytes. In fact, all associated-token-accounts use the "immutable owner" extensions, which adds another 5 bytes to the account size.

This means token accounts are either accounts with a length of 165, OR accounts with the number "2" as their 165th byte, so you can do:

const filters = [
        {
            memcmp: {
                offset: 0,
                bytes: address,
            },
        },
        {
            memcmp: {
                offset: 165,
                bytes: "3", // the number 2 as base58, which means AccountType::Account
            }
        },
    ];

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.