Multiple ERC20s or NFTs

Learn how to fetch the list of common token holders of multiple ERC20 tokens or NFTs (ERC721s & ERC1155s).

Topics

Best Practices

With nested queries, Airstack finds the intersection of common token holders of multiple ERC20 tokens or NFTs in the following order:

  1. Filtering token holders on the 1st outermost query

  2. Comparing token holders of 1st outermost query against the token holders on 2nd outermost query

  3. Comparing token holders of the 1st and 2nd outermost query against the token holders on the 3rd outermost query

  4. Comparing token holders of 1st, 2nd, ..., nth outermost query against the token holders on (n + 1)-th outermost query

Since the number of objects returned in the responses will be dependent on the number of token holders on the 1st outermost query, it's most efficient that you have the token with the least amount of holders on the 1st outermost query.

Suppose there are two tokens:

  • Token A: 100,000 holders

  • Token B: 1,000 holders.

If Token A is the input on the 1st outermost query, then the end result will be 100,000 objects in the response array.

On the other hand, if Token B is the input on the 1st outermost query, then the end result will be instead ONLY 1,000 objects in the response array.

The latter approach will be more efficient and easier for further formatting.

Common Holders of 2 ERC20 Tokens

Fetching

You can fetch the common holders of two given ERC20, e.g. USDT and USDC:

query GetCommonHoldersOfUSDTAndUSDC {
  TokenBalances(input: {filter: {tokenAddress: {_eq: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}}, blockchain: ethereum, limit: 200}) {
    TokenBalance {
      owner {
        tokenBalances(input: {filter: {tokenAddress: {_eq: "0xdAC17F958D2ee523a2206206994597C13D831ec7"}}, limit: 200}) {
          owner {
            addresses
          }
        }
      }
    }
  }
}

All the common holders' addresses will be returned inside the innermost owner.addresses field.

Formatting

To get the list of all holders in a flat array, use the following format function:

const formatFunction = (data) =>
  data?.TokenBalances?.TokenBalance?.map(
    ({ owner }) => owner?.tokenBalances?.[0]?.owner?.addresses
  )
    .filter(Boolean)
    .flat(1)
    .filter((address, index, array) => array.indexOf(address) === index) ?? [];

The final result will the the list of all common holders in an array:

[
  "0xc77d249809ae5a118eef66227d1a01a3d62c82d4",
  "0x3291e96b3bff7ed56e3ca8364273c5b4654b2b37",
  "0xe348c7959e47646031cea7ed30266a6702d011cc",
  // ...other token holders
  "0xa69babef1ca67a37ffaf7a485dfff3382056e78c",
  "0x46340b20830761efd32832a74d7169b29feb9758",
  "0x2008b6c3d07b061a84f790c035c2f6dc11a0be70"
]

Common Holders of 2 NFTs

Fetching

You can fetch the common holders of two given NFTs, e.g. BAYC and Moonbirds:

query GetCommonHoldersOfBAYCAndMoonBirds {
  TokenBalances(input: {filter: {tokenAddress: {_eq: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"}}, blockchain: ethereum, limit: 200}) {
    TokenBalance {
      owner {
        tokenBalances(input: {filter: {tokenAddress: {_eq: "0x23581767a106ae21c074b2276D25e5C3e136a68b"}}, limit: 200}) {
          owner {
            addresses
          }
          tokenId
        }
      }
    }
  }
}

All the common holders' addresses will be returned inside the innermost owner.addresses field and tokenId is optional for determining which NFT is specifically held by the user as there can be multiple NFTs held by a single user.

Formatting

To get the list of all holders in a flat array, use the following format function:

const formatFunction = (data) =>
  data?.TokenBalances?.TokenBalance?.map(
    ({ owner }) => owner?.tokenBalances?.[0]?.owner?.addresses
  )
    .filter(Boolean)
    .flat(1)
    .filter((address, index, array) => array.indexOf(address) === index) ?? [];

The final result will the the list of all common holders in an array:

[
  "0xc77d249809ae5a118eef66227d1a01a3d62c82d4",
  "0x3291e96b3bff7ed56e3ca8364273c5b4654b2b37",
  "0xe348c7959e47646031cea7ed30266a6702d011cc",
  // ...other token holders
  "0xa69babef1ca67a37ffaf7a485dfff3382056e78c",
  "0x46340b20830761efd32832a74d7169b29feb9758",
  "0x2008b6c3d07b061a84f790c035c2f6dc11a0be70"
]

Common Holders of NFT That Held A Minimum Amount of ERC20 Token

Fetching

You can fetch common holders of an NFT with a specific amount held for the ERC20, e.g. Moonbirds holders with more than 10 USDT:

query GetCommonHoldersOfMoonbirdsAndMoreThanTenUSDT {
  TokenBalances(input: {filter: {tokenAddress: {_eq: "0x23581767a106ae21c074b2276D25e5C3e136a68b"}}, blockchain: ethereum, limit: 200}) {
    TokenBalance {
      owner {
        tokenBalances(
          input: {
            filter: {
              tokenAddress: {_eq: "0xdAC17F958D2ee523a2206206994597C13D831ec7"},
              formattedAmount: {_gt: 10}
            },
            limit: 200
          }
        ) {
          owner {
            addresses
          }
        }
      }
    }
  }
}

All the common holders' addresses will be returned inside the innermost owner.addresses field.

Formatting

To get the list of all holders in a flat array, use the following format function:

const formatFunction = (data) =>
  data?.TokenBalances?.TokenBalance?.map(
    ({ owner }) => owner?.tokenBalances?.[0]?.owner?.addresses
  )
    .filter(Boolean)
    .flat(1)
    .filter((address, index, array) => array.indexOf(address) === index) ?? [];

The final result will the the list of all common holders in an array:

[
  "0xc77d249809ae5a118eef66227d1a01a3d62c82d4",
  "0x3291e96b3bff7ed56e3ca8364273c5b4654b2b37",
  "0xe348c7959e47646031cea7ed30266a6702d011cc",
  // ...other token holders
  "0xa69babef1ca67a37ffaf7a485dfff3382056e78c",
  "0x46340b20830761efd32832a74d7169b29feb9758",
  "0x2008b6c3d07b061a84f790c035c2f6dc11a0be70"
]

Common Holders of A Token on Ethereum and A Token on Polygon (Cross-Chain)

Fetching

You can fetch common holders of NFT from different chains, e.g. BAYC on Ethereum and Cuddleverse on Polygon:

query GetCommonHoldersOfBAYCAndMoonBirds {
  TokenBalances(input: {filter: {tokenAddress: {_eq: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"}}, blockchain: ethereum, limit: 200}) {
    TokenBalance {
      owner {
        tokenBalances(input: {filter: {tokenAddress: {_eq: "0xb59bd2c3f24afa4a3177d0e886abe072ef9c8eb0"}}, blockchain: polygon, limit: 200}) {
          owner {
            addresses
          }
          tokenId
        }
      }
    }
  }
}

All the common holders' addresses will be returned inside the innermost owner.addresses field.

Formatting

To get the list of all holders in a flat array, use the following format function:

const formatFunction = (data) =>
  data?.TokenBalances?.TokenBalance?.map(
    ({ owner }) => owner?.tokenBalances?.[0]?.owner?.addresses
  )
    .filter(Boolean)
    .flat(1)
    .filter((address, index, array) => array.indexOf(address) === index) ?? [];

The final result will the the list of all common holders in an array:

[
  "0xc77d249809ae5a118eef66227d1a01a3d62c82d4",
  "0x3291e96b3bff7ed56e3ca8364273c5b4654b2b37",
  "0xe348c7959e47646031cea7ed30266a6702d011cc",
  // ...other token holders
  "0xa69babef1ca67a37ffaf7a485dfff3382056e78c",
  "0x46340b20830761efd32832a74d7169b29feb9758",
  "0x2008b6c3d07b061a84f790c035c2f6dc11a0be70"
]

Common Holders of More Than 2 ERC20 Tokens or NFTs

Fetching

Fetching common holders for more than 2 ERC20 tokens or NFTs works the same way as fetching only 2 ERC20 tokens or NFTs with the addition of more token address parameters and nesting:

query GetCommonHoldersOfMoreThanTwoTokensOrNfts {
  TokenBalances(
    input: {filter: {tokenAddress: {_eq: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}}, blockchain: ethereum, limit: 200}
  ) {
    TokenBalance {
      owner {
        tokenBalances(
          input: {filter: {tokenAddress: {_eq: "0xdAC17F958D2ee523a2206206994597C13D831ec7"}}, limit: 200}
        ) {
          owner {
            tokenBalances(
              input: {filter: {tokenAddress: {_eq: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"}}, limit: 200, blockchain: polygon}
            ) {
              owner {
                addresses
              }
            }
          }
        }
      }
    }
  }
}

All the common holders' addresses will be returned inside the innermost owner.addresses field.

Formatting

To get the list of all holders in a flat array, use the following format function:

const formatFunction = (data) =>
  data?.TokenBalances?.TokenBalance?.map(
    ({ owner }) =>
      owner?.tokenBalances?.[0]?.owner?.tokenBalances?.[0]?.owner?.addresses
  )
    .filter(Boolean)
    .flat(1)
    .filter((address, index, array) => array.indexOf(address) === index) ?? [];

The final result will the the list of all common holders in an array:

[
  "0xc77d249809ae5a118eef66227d1a01a3d62c82d4",
  "0x3291e96b3bff7ed56e3ca8364273c5b4654b2b37",
  "0xe348c7959e47646031cea7ed30266a6702d011cc",
  // ...other token holders
  "0xa69babef1ca67a37ffaf7a485dfff3382056e78c",
  "0x46340b20830761efd32832a74d7169b29feb9758",
  "0x2008b6c3d07b061a84f790c035c2f6dc11a0be70"
]

Developer Support

If you have any questions or need help regarding fetching token holders of multiple ERC20s or NFTs, please join our Airstack's Telegram group.

More Resources

Last updated

#300: add-user-details

Change request updated