Written by
Hunter Davis
Published on
June 1, 2024
Copy link

All You Need to Know About Solana’s New DAS API

Overview

Retrieving NFTs and tokens on Solana is now simplified thanks to the introduction of the Digital Asset Standard (DAS) API. The DAS API, a recent addition to the Solana developers' toolkit, offers a unified interface for retrieving digital assets on Solana. Instead of dealing with multiple endpoints to interact with different asset types, developers can now leverage a single API to acquire the data they need for their applications.

This interactive guide will cover:

  1. An understanding of the available asset types on Solana.
  2. A comprehensive look at the methods provided by the DAS API.
  3. Demonstrations of real use cases for each endpoint that are easily customizable.

This guide will allow you to follow along each use case, and by the end, you will be equipped to utilize DAS masterfully.

Prerequisites

  • Node.js installed (v18.0 required to use built-in fetch)
  • Helius RPC
  • Basic knowledge of JavaScript

Environment Setup

  1. Create a project folder named functions.
  2. For each example, create a new file within this folder.

Asset Types

In the Solana ecosystem, an "asset" can be any digital item of value, such as tokens or non-fungible tokens (NFTs), that exist on the blockchain. Solana supports a wide range of these assets. Understanding the type of data the DAS API returns when interacting with these assets is crucial.

Let's take a closer look at each asset type.

Non-Fungible

Non-Fungible assets follow the standard NFT model, storing metadata in a token account. This data resides in a Program Derived Address (PDA), an address owned by a program and not a specific user. They feature a Metadata PDA and a Master Edition PDA on the Solana blockchain.

Fungible

Fungible assets are SPL tokens with limited metadata. They can represent tokens like USDC or community/project tokens. A token will conform to the Fungible standard if its decimal value is greater than 0 during creation.

Fungible Asset

Fungible Assets represent items rather than individual units. They can hold more metadata than a standard Fungible asset. If the decimal is set to 0 during the creation of a Fungible standard item, it transforms into a Fungible Asset standard.

Programmable Non-Fungible

Programmable Non-Fungible assets mirror the Non-Fungible standard but remain in a frozen token account. This state prevents users from burning, locking, or transferring programmable assets without interacting with the Token Metadata program. This standard was a response to the royalty debate on Solana.

Methods Available

The DAS API offers a variety of methods tailored to different use cases, including:

  1. getAsset: Retrieve an asset by its ID.
  2. searchAssets: Locate assets using various parameters.
  3. getAssetProof: Obtain a merkle proof for a compressed asset by its ID.
  4. getAssetsByGroup: Acquire a list of assets by a group key and value.
  5. getAssetsByOwner: Retrieve a list of assets owned by an address.
  6. getAssetsByCreator: Get a list of assets created by an address.
  7. getAssetsByAuthority: Find a list of assets with a specific authority.

For detailed information about each method, refer to our documentation.

1. Get Asset

The getAsset endpoint allows you to retrieve a specific asset by its ID. This ID can represent an on-chain token address or the ID on the merkle tree for compressed assets.

For more information, refer to the getAsset documentation.

Example

Suppose we want to fetch the metadata of the Rank 1 Claynosaurz asset. In this case, we need to locate the asset's ID, set up our function to make the call to the DAS, and arrange our response to parse the exact object we need from the results.

Follow the steps below:

  1. Start by setting up the function in a new file named “getAsset.js”:

const url = `https://rpc.helius.xyz/?api-key=`;

const getAsset = async () => {
// Code goes here 
};
getAsset();

     2. Next, set up your fetch and await operations. Include the required ID parameter in the body of your request:


const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'getAsset',
      params: {
	// Rank 1 Claynosaurz #7392
        id: 'B1rzqj4cEM6pWsrm3rLPCu8QwcXMn6H6bd7xAnk941dU',
      },
    }),
  });

In this step, we're sending a POST request to the DAS API with the asset's unique ID in the request body.

    3. Parse the results and display the metadata information:


const { result } = await response.json();
console.log("asset: ", result);

This code snippet parses the API response into JSON format and logs the result to the console.

You can then run the script using the command node getAsset.js.

Note: This method retrieves data for a single asset. If you need to search for a group of assets, the DAS API provides additional methods which we will discuss later.

Result

Executing this script will display the metadata of the specified asset:


asset:  {
  interface: 'Custom',
  id: 'B1rzqj4cEM6pWsrm3rLPCu8QwcXMn6H6bd7xAnk941dU',
  content: {
    '$schema': 'https://schema.metaplex.com/nft1.0.json',
    json_uri: 'https://nftstorage.link/ipfs/bafybeig2dp7oyauxdkhwduh274ekbl3cixyvbnky444qfewz3vfcauos6m/7391.json',
    files: [],
    metadata: { name: 'Claynosaurz #7392', symbol: 'DINO' }
  },
  authorities: [
    {
      address: 'B7B2g3WbdZMDV3YcDGRGhEt5KyWqDJZFwRR8zpWVEkUF',
      scopes: [Array]
    }
  ],
  compression: {
    eligible: false,
    compressed: false,
    data_hash: '',
    creator_hash: '',
    asset_hash: '',
    tree: '',
    seq: 0,
    leaf_id: 0
  },
  grouping: [
    {
      group_key: 'collection',
      group_value: '6mszaj17KSfVqADrQj3o4W3zoLMTykgmV37W4QadCczK'
    }
  ],
  royalty: {
    royalty_model: 'creators',
    target: null,
    percent: 0.05,
    basis_points: 500,
    primary_sale_happened: true,
    locked: false
  },
  creators: [
    {
      address: 'AoebZtN5iKpVyUBc82aouWhugVknLzjUmEEUezxviYNo',
      share: 0,
      verified: true
    },
    {
      address: '36tfiBtaDGjAMKd6smPacHQhe4MXycLL6f9ww9CD1naT',
      share: 100,
      verified: false
    }
  ],
  ownership: {
    frozen: false,
    delegated: false,
    delegate: null,
    ownership_model: 'single',
    owner: '4zdNGgAtFsW1cQgHqkiWyRsxaAgxrSRRynnuunxzjxue'
  },
  supply: null,
  mutable: true
}

This output provides detailed information about the asset, including the interface type, ID, metadata, creators, authorities, and ownership.

Full Code

Below is the complete code for fetching an asset using its ID:


const url = `https://rpc.helius.xyz/?api-key=`;

const getAsset = async () => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'getAsset',
      params: {
        id: 'B1rzqj4cEM6pWsrm3rLPCu8QwcXMn6H6bd7xAnk941dU',
      },
    }),
  });

  const { result } = await response.json();
  console.log("asset: ", result);
};

getAsset();

Ensure to replace <api-key> with your actual API key.

In this code, we define an asynchronous function, getAsset, which sends a POST request to the DAS API. We pass the asset's ID in the request body. Once the request completes, the function parses the response as JSON and prints the asset data to the console. Finally, we invoke the getAsset function to execute this process.

2. Get Asset Proof

The getAssetProof endpoint is used to retrieve an asset proof necessary for modifications to the compression program. Such modifications include actions like transfer, burn, update creator, update collection, and decompress of compressed assets.

For a detailed list of modifications using the asset proof, please refer to the documentation.

Example

To fetch the asset proof needed to modify a compressed asset, follow these steps:

  1. Begin by setting up the function in a new file named “getAssetProof.js”:

const url = `https://rpc.helius.xyz/?api-key=`;

const getAsset = async () => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'getAsset',
      params: {
        id: 'B1rzqj4cEM6pWsrm3rLPCu8QwcXMn6H6bd7xAnk941dU',
      },
    }),
  });

  const { result } = await response.json();
  console.log("asset: ", result);
};

getAsset();

      2. Proceed with setting up your fetch and await. Include your required ID parameter in the body of your request:


const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
		body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'getAssetProof',
      params: {
        id: 'JDuAmJjiiNKCfK9SyW1aQCNvhL7krhVWZbeijVupAz4i'
      },
    }),
  });

     3. Parse the result and extract the root:


const { proof } = await response.json();
console.log("Asset Proof: ", result);
const root = decode(proof.root);
console.log(root)

Here, we are extracting the root from the asset proof, which can then be used to make additional modifications to the compressed asset.

Run node getAssetProof.js in your terminal to get a return on the asset you set here.

Result

The output will include the asset proof information:


Assets Proof:  {
  root: 'FCdCNPGauQp1NqZbs1f4DDAWawHLbBqfD9LYjMy1fqH4',
  proof: [
    'EmJXiXEAhEN3FfNQtBa5hwR8LC5kHvdLsaGCoERosZjK',
    '7NEfhcNPAwbw3L87fjsPqTz2fQdd1CjoLE138SD58FDQ',
    '6dM3VyeQoYkRFZ74G53EwvUPbQC6LsMZge6c7S1Ds4ks',
    '34dQBtcjnCUoPVzZEmVPAMH7b3b8aD6GUB9aYS11AaWJ',
    '2VG5cKeBZdqozwhHGGzs13b9tzy9TXt9kPfN8MzSJ1Sm',
    'r1o8vR5KFHJeER7A1K7kBCjceDHnUbSwiFEqqmeAQSd',
    '88sRtuz1QHWhYEKtx1VamwwrmtDkb8vyDyUuqWCJrxoa',
    '9Y8Xa2qwARx7Mg6deJwP37UEX9BA2tM75N4f6vaGyBDU',
    'CKWwHXAcqoTptkjZkuKqhQ9iuajC8dK6f8eGpknesqWS',
    '4n9Z4eSKNZa1a4oA3sbFGowA7go9BV9WopLpA4KqnYdD',
    '6MJKrpnK1GbYsnEzwMRWStNGkTjAZF23NhzTQSQVXsD3',
    'HjnrJn5vBUUzpCxzjjM9ZnCPuXei2cXKJjX468B9yWD7',
    '4YCF1CSyTXm1Yi9W9JeYevawupkomdgy2dLxEBHL9euq',
    'E3oMtCuPEauftdZLX8EZ8YX7BbFzpBCVRYEiLxwPJLY2'
  ],
  node_index: 16384,
  leaf: '6YdZXw49M97mfFTwgQb6kxM2c6eqZkHSaW9XhhoZXtzv',
  tree_id: '2kuTFCcjbV22wvUmtmgsFR7cas7eZUzAu96jzJUvUcb7'
}
Full Code

Here is the complete code to fetch an asset proof using its ID:


const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetProof = async () => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'getAssetProof',
      params: {
        id: 'JDuAmJjiiNKCfK9SyW1aQCNvhL7krhVWZbeijVupAz4i'
      },
    }),
  });
  const { result } = await response.json();
  console.log("Asset Proof: ", result);
	const root = proof.root;
	console.log(root)
};
getAssetProof();

Make sure to replace <api-key> with your actual API key.

In this script, we define an asynchronous function, getAssetProof, which sends a POST request to the Helius API. We pass the asset's ID in the request body. Once the request is complete, the function parses the response into JSON and prints the asset proof and its root to the console. Finally, we call the getAssetProof function to execute this process.

3. Search Assets

The searchAssets method retrieves digital assets based on specified search parameters, providing a flexible approach to fetch data. It allows users to customize the search, thus providing a more detailed control over the assets returned.

Detailed parameters are available in the searchAssets documentation.

Example

Let’s go through an example where we want to display an assets image and name from a user wallet, that belongs to the Drip Haus collection, and only display the compressed items within it. For brevity of this tutorial, we will post this in a JSON file with the name and image of each Drip asset in the example wallet.

  1. Begin by creating a new file named searchAssets.js with the following asynchronous function:

const fs = require('fs');
const url = `https://rpc.helius.xyz/?api-key=`;

const searchAssets = async () => {
	// Code goes here
};
searchAssets();

     2. Implement the function to send a POST request with the specified search parameters. In this case we are using the compressed, owner address, and collection grouping parameters to achieve our desired result:


const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: "my-id",
      method: "searchAssets",
      params: {
        // Returning only compressed items.
        compressed: true,
        // Example wallet
        ownerAddress: "2k5AXX4guW9XwRQ1AKCpAuUqgWDpQpwFfpVFh3hnm2Ha",
        // Drip Haus collection ID.
        grouping: [
          "collection",
          "DRiP2Pn2K6fuMLKQmt5rZWyHiUZ6WK3GChEySUpHSS4x"
        ],
        page: 1,
      },
    }),
  });

    3. Parse the response, group assets by their ID, and handle potential duplicates by detailing the exact items we need from the response of each item:


const { result } = await response.json();

  const groupedResults = [];

  for (let i = 0; i < result.items.length; i++) {
    const asset = {
      id: result.items[i].id,
      name: result.items[i].content.metadata.name,
      json_uri: result.items[i].content.json_uri,
    };

    4. The next function is finding any repeats here and removing them from our list, you can decide to not use this function if you want to show repeat assets. This will also add to a new asset group when it is determined it’s not a repeat:


const existingGroup = groupedResults.find(group => group.id === asset.id);
		if (existingGroup) {
      // Add the asset to the existing group
      existingGroup.assets.push(asset);
    } else {
      // Create a new group for the asset
      const newGroup = {
        id: asset.id,
        assets: [asset],
      };
  // Add the new group to the grouped results
      groupedResults.push(newGroup);
    }
  }

   5. Save the search results into a JSON file named searchResults.json:


const json = JSON.stringify(groupedResults, null, 2);

// Write the JSON to a file
fs.writeFileSync('searchResults.json', json);

console.log('Results saved to results.json');

To execute the script, run node searchAssets.js. This will populate the searchResults.json file with the search results.

Result

[
  {
    "id": "4XSuZ2JaCPYA76EomCd1mZCtrjkx4F4sdcepBgBF2LKE",
    "assets": [
      {
        "id": "4XSuZ2JaCPYA76EomCd1mZCtrjkx4F4sdcepBgBF2LKE",
        "name": "MOMENT",
        "json_uri": "https://arweave.net/3DniodKpcCTio-GlyB4GMjdA4c5epHzYQecdLZctt5s"
      }
    ]
  },
  {
    "id": "AJw2QNwWMLWTuUTiUWYpEyGjUQSPB5rYEcbn5uiBjQ2g",
    "assets": [
      {
        "id": "AJw2QNwWMLWTuUTiUWYpEyGjUQSPB5rYEcbn5uiBjQ2g",
        "name": "\"Kokoro\" (心)",
        "json_uri": "https://arweave.net/PyPo4Zr4iWH2iRROBDtZTFZv3qWw1dVcrYPq4A_6ttM"
      }
    ]
  }, ...
}
Full Code

const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');

const searchAssets = async () => {
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: "my-id",
      method: "searchAssets",
      params: {
        // Returning only compressed items.
        compressed: true,
        // Example wallet
        ownerAddress: "2k5AXX4guW9XwRQ1AKCpAuUqgWDpQpwFfpVFh3hnm2Ha",
        // Drip Haus collection ID.
        grouping: [
          "collection",
          "DRiP2Pn2K6fuMLKQmt5rZWyHiUZ6WK3GChEySUpHSS4x"
        ],
        page: 1,
      },
    }),
  });
  const { result } = await response.json();

  const groupedResults = [];

  for (let i = 0; i < result.items.length; i++) {
    const asset = {
      id: result.items[i].id,
      name: result.items[i].content.metadata.name,
      json_uri: result.items[i].content.json_uri,
    };

    // Find an existing group for the asset if it exists
    const existingGroup = groupedResults.find(group => group.id === asset.id);

    if (existingGroup) {
      // Add the asset to the existing group
      existingGroup.assets.push(asset);
    } else {
      // Create a new group for the asset
      const newGroup = {
        id: asset.id,
        assets: [asset],
      };

      // Add the new group to the grouped results
      groupedResults.push(newGroup);
    }
  }

  // Convert the grouped results to JSON
  const json = JSON.stringify(groupedResults, null, 2);

  // Write the JSON to a file
  fs.writeFileSync('searchResults.json', json);

  console.log('Results saved to results.json');
};

searchAssets();

Make sure to replace <api-key> with your actual API key.

In this script, we define an asynchronous function, searchAssets, which sends a POST request. It uses various search parameters in the request body, such as compression, owner address, and collection ID. Once the request is complete, the function parses the response into JSON and extracts the relevant asset information (ID, name, JSON URI). It groups these assets by ID and saves this organized data into a JSON file named searchResults.json. Finally, we call the searchAssets function to initiate this process.

4. Get Assets by Owner

The getAssetsByOwner endpoint provides a list of digital assets owned by a specific address. This is currently the fastest way to retrieve specific ownership information for digital assets using Helius.

Example

To fetch the assets owned by a certain address, follow these steps:

  1. Begin by setting up the function in a new file named “getAssetsByOwner.js”:

const fs = require('fs');
const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetsByOwner = async () => {
  // Code goes here
};
getAssetsByOwner();
  1. Set up your fetch and await, and include your parameters in the body of your request, for this endpoint we are using ownerAddress:

const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'hunter-test',
      method: 'getAssetsByOwner',
      params: {
					// Example wallet
        ownerAddress: '2k5AXX4guW9XwRQ1AKCpAuUqgWDpQpwFfpVFh3hnm2Ha',
        page: 1
      },
    }),
  });
  1. Then, parse your response and extract the necessary asset information to post to the JSON. We will use the asset ID, name, and json URI to group to the wallet we are searching:

const { result } = await response.json();
 const groupedResults = {};

  for (let i = 0; i < result.items.length; i++) {
    const ownerAddress = result.items[i].owner.address;
    const asset = {
      id: result.items[i].id,
      name: result.items[i].content.metadata.name,
      json_uri: result.items[i].content.json_uri,
    };

    if (groupedResults.hasOwnProperty(ownerAddress)) {
      // Add the asset to the existing group
      groupedResults[ownerAddress].assets.push(asset);
    } else {
      // Create a new group for the owner
      groupedResults[ownerAddress] = {
        assets: [asset],
      };
    }
  }
  1. We can now set up our function to post to a JSON:

// Convert the grouped results to JSON
  const json = JSON.stringify(groupedResults, null, 2);

  // Write the JSON to a file
  fs.writeFileSync('results.json', json);

  console.log('Results saved to results.json');
Result

The output will include the digital assets owned by the specified address:


{
  "2k5AXX4guW9XwRQ1AKCpAuUqgWDpQpwFfpVFh3hnm2Ha": {
    "assets": [
      {
        "id": "7Qj3QGCqRChr3uBR4R756usz2eoPSisoWcPfeozY7Bo",
        "name": "",
        "json_uri": "https://nftstorage.link/ipfs/bafybeiewlaxjeredwgsboqqha2ww25g46dgrqs6lwn5cooq3evsvye33iy/3071.json"
      },
      {
        "id": "8W1Dx9vhyQ8fNyi6oVu2EFbZHo2kKpcRxMVbQ3em3ne",
        "name": "Compass Rose #2745",
        "json_uri": "https://shdw-drive.genesysgo.net/HqhFDmVhqqN23g4soMd8UzrfLEXT8GsjWtWaqxfm9A2x/2745.json"
      },
      {
        "id": "Ba5qsjq5LryLPZ1e6AVqwPzB5LBBBzGWvvRQww4KCiG",
        "name": "Foxy Pixel Demon #423",
        "json_uri": "https://cdn.secretsphinx.io/ml/1c22a51697c644a8e45f603ed884ade7.json"
      },
      {
        "id": "Hmid2Dhi3zLV7AxCAibQ8n4nviWdPv2ZvUeZq4N33oe",
        "name": "",
        "json_uri": "https://nftstorage.link/ipfs/bafybeiewlaxjeredwgsboqqha2ww25g46dgrqs6lwn5cooq3evsvye33iy/140.json"
      }, ...
}
Full Code

Here is the complete code to fetch the assets owned by a specific address:


const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetsByOwner = async () => {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'hunter-test',
      method: 'getAssetsByOwner',
      params: {
					// Example wallet
        ownerAddress: '2k5AXX4guW9XwRQ1AKCpAuUqgWDpQpwFfpVFh3hnm2Ha',
        page: 1,
      },
    }),
  });
 const { result } = await response.json();
 const groupedResults = {};

  for (let i = 0; i < result.items.length; i++) {
    const ownerAddress = result.items[i].owner.address;
    const asset = {
      id: result.items[i].id,
      name: result.items[i].content.metadata.name,
      json_uri: result.items[i].content.json_uri,
    };

    if (groupedResults.hasOwnProperty(ownerAddress)) {
      // Add the asset to the existing group
      groupedResults[ownerAddress].assets.push(asset);
    } else {
      // Create a new group for the owner
      groupedResults[ownerAddress] = {
        assets: [asset],
      };
    }
  }

  // Convert the grouped results to JSON
  const json = JSON.stringify(groupedResults, null, 2);

  // Write the JSON to a file
  fs.writeFileSync('results.json', json);

  console.log('Results saved to results.json');
};

getAssetsByOwner();

Make sure to replace <api-key> with your actual API key.

In this script, we define an asynchronous function, getAssetsByOwner, which sends a POST request to the Helius API. We pass the owner's address in the request body. Once the request is complete, the function parses the response into JSON and prints the assets owned by the specified address to the console. Finally, we call the getAssetsByOwner function to execute this process.

5. Get Assets by Group

The getAssetsByGroup endpoint is used to retrieve digital assets associated with a specific collection ID. This endpoint is crucial when you need to fetch items specific to a collection or require a token-gated dApp to be associated with a certain on-chain collection.

Example

In this scenario, we are going to set up a collection snapshot for SMB’s. It's important to parse the response to extract the asset owner from the ownership object, as detailed in our documentation. The objective is to filter for owners of multiple SMBs, and only show them once in our JSON, effectively creating a “snapshot”.

  1. Start by setting up the function in a new file named “getAssetsByGroup.js”. Make use of a Set to ensure we only store unique owners:

const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');
const uniqueOwners = new Set();

const getAssetsByGroup = async () => {
		 // Code goes here
};
getAssetsByGroup();
  1. Proceed by setting up your fetch and await. Set your parameters listed above in the body of your request. Set the groupKey and groupValue in the request. Initialize page to 1 and hasMoreResults to true to set up for pagination. This will later set up a pagination function to return false if the results for page are less than 1000 (the max limit per request):

let page = 1;
  let hasMoreResults = true;

  while (hasMoreResults) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'my-id',
        method: 'getAssetsByGroup',
        params: {
          groupKey: 'collection',
          groupValue: 'SMBtHCCC6RYRutFEPb4gZqeBLUZbMNhRKaMKZZLHi7W',
          page,
          limit: 1000,
        },
      }),
    });
    
  1. Parse the results to extract the owner string needed to create the snapshot of the current owners:

const { result } = await response.json();
// Add each owner to the Set, automatically discarding duplicates
result.items.forEach(item => uniqueOwners.add(item.ownership.owner));    
  1. Set up pagination by increasing the page parameter if there are 1000 results. If there are fewer, set hasMoreResults to false to stop the pagination:

if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }
     
  1. Now we can set our owners to be an array, and setup our root value to post to a JSON with the number of holders, and each owner wallet that is unique:

const uniqueOwnersArray = Array.from(uniqueOwners);
  
  const root = {
    count: uniqueOwners.size,
    owners: uniqueOwnersArray
  };
  1. Convert the Set of unique owners to an array. Set up the root value to post to a JSON with the number of holders and each unique owner wallet:

fs.writeFile('./ownerResults.json', jsonResult, 'utf8', (err) => {
    if (err) {
      console.error("Error writing JSON file:", err);
    } else {
      console.log("JSON file saved successfully.");
    }
  });
console.log("Total number of unique owners:", uniqueOwners.size);
};

You can then run the command node getAssetsByGroup.js to populate the "ownerResults.json" file with your results.

Result

Your result would be a JSON equivalent of a holder snapshot, with the count of unique owners and a list of all unique owners:


{
  "count": 2785,
  "owners": [
    "6VqzFgtrJb33nhvbug4KZoUx8p65dD2iT1QuAeAQgYiw",
    "5Xeb43ASEa64b9i9owcLB4yrNbUw1oMiTpcWsAdXN8qG",
    "Cb355XH2WGPeQUGTTXWiQZT4nhnyHCPkhScDezUGhXQF",
    "Fuu7xpK3mWqpqPLTHaxF7pU2czkBESKyg3r2Lm2F3AWz",
    "1BWutmTvYPwDtmw9abTkS4Ssr8no61spGAvW1X6NDix",
    "86tCSKzryE5KvbTmMXN9tkxyn8GNr4z54DnNqrcZwYuy",
    "D2DYL5sdxBCpauvKs1oyQkkSm2B9rFzzGowMacv3Q58z",
		// Additional results ...
]
}

Full Code

const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');
const uniqueOwners = new Set();

const getAssetsByGroup = async () => {
  let page = 1;
  let hasMoreResults = true;

  while (hasMoreResults) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'my-id',
        method: 'getAssetsByGroup',
        params: {
          groupKey: 'collection',
          groupValue: 'SMBtHCCC6RYRutFEPb4gZqeBLUZbMNhRKaMKZZLHi7W',
          page,
          limit: 1000,
        },
      }),
    });

    const { result } = await response.json();
    // Add each owner to the Set, automatically discarding duplicates
    result.items.forEach(item => uniqueOwners.add(item.ownership.owner));

    if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }

  // Convert Set to Array for stringification
  const uniqueOwnersArray = Array.from(uniqueOwners);
  
  const root = {
    count: uniqueOwners.size,
    owners: uniqueOwnersArray
  };
  
  const jsonResult = JSON.stringify(root, null, 2);

  fs.writeFile('./ownerResults.json', jsonResult, 'utf8', (err) => {
    if (err) {
      console.error("Error writing JSON file:", err);
    } else {
      console.log("JSON file saved successfully.");
    }
  });
};
console.log("Total number of unique owners:", uniqueOwners.size);

getAssetsByGroup();

Make sure to replace <api-key> with your actual API key.

You've now learned how to retrieve digital assets associated with a specific collection ID using the getAssetsByGroup endpoint. By effectively using the pagination and Set data structure to ensure unique owner entries, you can create a snapshot of current holders of any collection with an on-chain ID.

6. Get Assets by Creator

The getAssetsByCreator endpoint is used to retrieve assets created by a specific public key address. This endpoint is useful when you want to find assets related to a specific artist or project on Solana.

Example

For this example, we are going to return assets created by Zen0. We can use the creator address and set the onlyVerified parameter to true to only retrieve assets created by a verified wallet. We will parse the results to display each asset's ID and its owner.

  1. Start by setting up the function in a new file named "getAssetsByCreator.js". We will use the fs module to post our results to a JSON file:

const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');


const getAssetsByCreator = async () => {
  // Code goes here
};
getAssetsByCreator();
  1. Now, we can set up the fetch request and await the response. Set the necessary parameters in the body of the request, including the creatorAddress and onlyVerified:

let page = 1;
  let allResults = [];
  let hasMoreResults = true;

  while (hasMoreResults) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'my-id',
        method: 'getAssetsByCreator',
        params: {
          creatorAddress: 'zvrsoq2LbNxekPqLK1v8DsLgeC4LHxMQL52beX8Ktn8',
          onlyVerified: true,
          page,
          limit: 1000,
        },
      }),
    });
    

Don't forget to replace <creator-address> with the actual address of the creator whose assets you wish to retrieve.

  1. Parse the response to extract the result and store it in an array:

const { result } = await response.json();
allResults = allResults.concat(result.items);

if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }
  

    
  1. Group the assets based on the ownership ID (since a single person can own multiple assets), and display the assets owned by each ID. If a new owner is detected, a new group will be created in the JSON file:

const groupedResults = {};

  for (let i = 0; i < allResults.length; i++) {
    const ownershipId = allResults[i].ownership.owner;
    const asset = {
      id: allResults[i].id,
      ownershipId: ownershipId,
    };

    if (groupedResults.hasOwnProperty(ownershipId)) {
      // Add the asset to the existing group
      groupedResults[ownershipId].assets.push(asset);
    } else {
      // Create a new group for the ownership ID
      groupedResults[ownershipId] = {
        assets: [asset],
      };
    }
  }
  1. The next step is to structure the results. In this case, we're interested in the address of the NFT and the owner of each returned asset. We use root to specify the length of the return and store our modified results for output:

// Create new array to hold the desired properties
 const modifiedResults = totalResults.map(item => ({
    id: item.id,
    owner: item.ownership.owner
  }));
  // Create a root object to hold the count and the results
  const root = {
    count: modifiedResults.length,
    results: modifiedResults
  };
  1. Finally, we save the results to a file named "creatorResults.json" using the fs module:

// Convert the grouped results to JSON
  const json = JSON.stringify(groupedResults, null, 2);

  // Write the JSON to a file
  fs.writeFileSync('creatorResults.json', json);

  console.log('Results saved to results.json');
  

Finally, run the command node getAssetsByCreator.js to execute the script and populate the "creatorResults.json" file with the retrieved results.

Result

The resulting JSON file contains the public key address of each owner and the respective assets they own. Each asset is represented by its ID, providing a clear snapshot of the ownership status of the assets created by the specific creator.


{
  "ZVcBfkk3Be8QMn4rmQL2VtP2WJQp9wpU8udFwrTGA22": {
    "assets": [
      {
        "id": "KvbtDebCi6BGSFuafJWZpwV5mt1XYng13Pt2vh4G2Qa",
        "ownershipId": "ZVcBfkk3Be8QMn4rmQL2VtP2WJQp9wpU8udFwrTGA22"
      },
      {
        "id": "28bXCZaETv6ihBGbtavDkedzRcWZPbmDG5VYXxSdmZBc",
        "ownershipId": "ZVcBfkk3Be8QMn4rmQL2VtP2WJQp9wpU8udFwrTGA22"
      },
      {
        "id": "2H4txSfZwV3nX4fJ1D8dPNHiafn247iQ8vtq3y3UYdpe",
        "ownershipId": "ZVcBfkk3Be8QMn4rmQL2VtP2WJQp9wpU8udFwrTGA22"
      },
      {
        "id": "5oauJRPWbboJVUBms2mqBF2wMQGMSCFScgX18SawyKV3",
        "ownershipId": "ZVcBfkk3Be8QMn4rmQL2VtP2WJQp9wpU8udFwrTGA22"
      }, 
	"Fb7pzfL6SLDNi79WSWkZm6zKA94LYNsYKDJC7GNvkZaC": {
    "assets": [
      {
        "id": "2jLjf8Vmbisu5eac1NgSiDcqkcJfR8R9LSGjD6SExC99",
        "ownershipId": "Fb7pzfL6SLDNi79WSWkZm6zKA94LYNsYKDJC7GNvkZaC"
      },
      {
        "id": "2rVtLCHWp1hpUc7YVoAkFrSL3iwGQDcCzAvboCvrq5kT",
        "ownershipId": "Fb7pzfL6SLDNi79WSWkZm6zKA94LYNsYKDJC7GNvkZaC"
      },
      {
        "id": "6QXfTMwuaKTMZyFYoUwNWpaT692Z9Zqg94WE9MTi4J2p",
        "ownershipId": "Fb7pzfL6SLDNi79WSWkZm6zKA94LYNsYKDJC7GNvkZaC"
      }
    ]
  }, ...
}
  

The 'ownershipId' represents the owner's public key address on the Solana blockchain, and the assets are the NFTs linked to the address.

Full Code

const fs = require('fs');

const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetsByCreator = async () => {
  let page = 1;
  let allResults = [];
  let hasMoreResults = true;

  while (hasMoreResults) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'my-id',
        method: 'getAssetsByCreator',
        params: {
          creatorAddress: 'zvrsoq2LbNxekPqLK1v8DsLgeC4LHxMQL52beX8Ktn8',
          onlyVerified: true,
          page,
          limit: 1000,
        },
      }),
    });

    const { result } = await response.json();
    allResults = allResults.concat(result.items);

    if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }

  // Create an object to store the grouped results
  const groupedResults = {};

  for (let i = 0; i < allResults.length; i++) {
    const ownershipId = allResults[i].ownership.owner;
    const asset = {
      id: allResults[i].id,
      ownershipId: ownershipId,
    };

    if (groupedResults.hasOwnProperty(ownershipId)) {
      // Add the asset to the existing group
      groupedResults[ownershipId].assets.push(asset);
    } else {
      // Create a new group for the ownership ID
      groupedResults[ownershipId] = {
        assets: [asset],
      };
    }
  }

  // Convert the grouped results to JSON
  const json = JSON.stringify(groupedResults, null, 2);

  // Write the JSON to a file
  fs.writeFileSync('creatorResults.json', json);

  console.log('Results saved to results.json');
};

getAssetsByCreator();
  

Make sure to replace <api-key> with your actual API key.

In this example we are making an asynchronous request to the DAS API for the “getAssetsByCreator” endpoint. Then, we are passing in our creator address and implying that they are a verified creator. Finally, we are then setting up our response to parse the owner of each asset that is under the creator address, and posting to an external JSON file.

Overall, this is a valuable tool for anyone interested in tracking or analyzing the movement of assets on the Solana blockchain, especially those tied to a specific creator or project.

7. Get Assets by Authority

The function getAssetsByAuthority fetches assets associated with a specific update authority. An update authority is an address that possesses the rights to modify a collection. This feature becomes particularly valuable when you need to fetch a set of assets in cases where a collection ID doesn't exist. Additionally, it offers the advantage of retrieving a larger set of assets associated with an authority address, extending beyond the confines of a single collection ID.

Example

In the following example, we'll retrieve each collection NFT and their respective owner from Taiyo Robotics, Pilots, and Infants. Given that they all share a common update authority, it's possible to return all the results pertinent to the project.

This is an upgrade from the getAssetsByGroup function, as you can group assets linked to a set authority CDgbhX61QFADQAeeYKP5BQ7nnzDyMkkR3NEhYF2ETn1k.

  1. Initiate by setting up the function in a newly created file named "getAssetsByAuthority.js":

const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetsByAuthority = async () => {
  // Code goes here
};
getAssetsByAuthority();
  
  1. Configure the fetch request and incorporate necessary parameters in the body, such as authorityAddress, page, and limit:

let page = 1;
let hasMoreResults = true;

while (hasMoreResults) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'my-id',
        method: 'getAssetsByAuthority',
        params: {
          authorityAddress: 'CDgbhX61QFADQAeeYKP5BQ7nnzDyMkkR3NEhYF2ETn1k',
          page,
          limit: 1000,
        },
      }),
    });
    

  

Ensure to substitute <authority-address> with the actual authority address whose assets you wish to extract.

     3. It's now possible to arrange our return to cycle through results if there are 1000 results.

     If the number of results is less, the flag will set to false, and terminating the pagination, in which hasMoreResults will return false.


const { result } = await response.json();
    totalResults.push(...result.items);

    if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }
  1. Handle the assets to derive the required information. Here, we aim to display the ID of the asset and its owner. We'll also craft a root object to contain the count of assets and the processed asset array:

// Create new array to hold the desired properties
 const modifiedResults = totalResults.map(item => ({
    id: item.id,
    owner: item.ownership.owner
  }));
  // Create a root object to hold the count and the results
  const root = {
    count: modifiedResults.length,
    results: modifiedResults
  };
  1. We're now prepared to set our returned items to post to a JSON called authorityResults.json, and we'll log once the process concludes.

const jsonResult = JSON.stringify(root, null, 2);

  fs.writeFile('./authorityResults.json', jsonResult, 'utf8', (err) => {
    if (err) {
      console.error("Error writing JSON file:", err);
    } else {
      console.log("JSON file saved successfully.");
    }
  });
console.log("Process completed.");
console.timeEnd("getAssetsByAuthority");


Lastly, execute the command node getAssetsByAuthority.js to run the script and populate the "authorityResults.json" file with the fetched results.

Result

The output for the getAssetsByAuthority endpoint includes a count of assets and an array of objects, each representing an asset and containing its ID and owner. Here's a sample:


{
  "count": 37330,
  "results": [
    {
      "id": "HmwL6uy7gQcXv74Mi8xF5G9mymZNd9rnUCpUFwSx1DX1",
      "owner": "5gt59Q14FEhT8LuETjLyiNHRkqCQf5hQsPP58ucBy5oC"
    },
    {
      "id": "HmztH56n4GD3p3EdgMKdzj8x21LjFQk4Bcp2YAQD8Urx",
      "owner": "H3AkHZHfcqGCcJpBn3FJWe52LcLxFMJQoZvZ6XyApFWf"
    },
    {
      "id": "Hn1NZXCaAxcr1btsaDm3eZRtLNbamy9FyrcVGLbZ2k5w",
      "owner": "AEqhqiQZBBa3dPTY1GwGsZJJnf5vzRKJpdXv6xzakfx8"
    }, ...
}
Full Code

const fs = require('fs');
const url = `https://rpc.helius.xyz/?api-key=`;

const totalResults = [];

const getAssetsByAuthority = async () => {
  let page = 1;
  let hasMoreResults = true;

  while (hasMoreResults) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 'my-id',
        method: 'getAssetsByAuthority',
        params: {
          authorityAddress: 'CDgbhX61QFADQAeeYKP5BQ7nnzDyMkkR3NEhYF2ETn1k',
          page,
          limit: 1000,
        },
      }),
    });

    const { result } = await response.json();
    totalResults.push(...result.items);

    if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }  
  // Create new array to hold the desired properties
  const modifiedResults = totalResults.map(item => ({
    id: item.id,
    owner: item.ownership.owner
  }));
  
  // Create a root object to hold the count and the results
  const root = {
    count: modifiedResults.length,
    results: modifiedResults
  };
  
  // Stringify with 2 spaces indentation for readability
  const jsonResult = JSON.stringify(root, null, 2);

  fs.writeFile('./authorityResults.json', jsonResult, 'utf8', (err) => {
    if (err) {
      console.error("Error writing JSON file:", err);
    } else {
      console.log("JSON file saved successfully.");
    }
  });
console.log("Process completed.");
console.timeEnd("getAssetsByAuthority");
};
getAssetsByAuthority()

Ensure to substitute <api-key> with your actual API key.

In this example, we employ the "getAssetsByAuthority" method to return all assets tied to an authority address across 3 collections. We utilize only page and limit as supplementary parameters for the request. Subsequently, we parse the results to extract the assets under the authority, and store them in an external JSON file.

Conclusion

You should now have a comprehensive understanding of how to use the Digital Asset Standard (DAS) API, along with several practical applications for each method. This marks a significant departure from previous practices of needing multiple endpoints to fetch specific asset information.

These methods cater to both compressed and regular assets, offering a unified interface for making these requests in various ways.

For more information, you can always refer to our extensive documentation on the Digital Asset Standard (DAS) API, and explore our Open API widget that provides a detailed breakdown of request and response parameters.

As always, we invite you to join our Discord community. Don't hesitate to ping me there if you have any queries!