НОВОЕ: Helius приобретает Light Protocol
DAS NFT API для Solana
Блог/Обновления

Всё, что нужно знать о новом DAS API для Solana

Руководитель по работе с разработчикамиHunter Davis в LinkedIn
Чтение: 15 мин

Обзор

Получать NFT и токены в Solana стало проще благодаря API стандарта цифровых активов Digital Asset Standard (DAS). DAS API — это новое дополнение к набору инструментов разработчиков Solana с единым интерфейсом для получения цифровых активов в Solana. Вместо нескольких эндпоинтов для работы с разными типами активов разработчики теперь могут получать необходимые приложениям данные через один API.

В этом интерактивном руководстве мы рассмотрим:

  1. Доступные типы активов в Solana.
  2. Все методы DAS API.
  3. Реальные сценарии использования каждого эндпоинта, которые легко адаптировать под свои задачи.

Вы сможете последовательно выполнить каждый пример и в итоге научитесь уверенно работать с DAS.

Предварительные требования

  • Установленный Node.js (для встроенного fetch требуется версия 18.0)
  • Helius RPC
  • Базовое знание JavaScript

Настройка окружения

  1. Создайте папку проекта с именем functions.
  2. Для каждого примера создавайте в этой папке новый файл.

Типы активов

В экосистеме Solana «активом» может быть любой имеющий ценность цифровой объект в блокчейне, например токен или невзаимозаменяемый токен (NFT). Solana поддерживает множество таких активов. Важно понимать, какие данные возвращает DAS API при работе с ними.

Рассмотрим каждый тип активов подробнее.

Невзаимозаменяемые активы

Невзаимозаменяемые активы соответствуют стандартной модели NFT и хранят метаданные в токен-аккаунте. Эти данные находятся по адресу, производному от программы (Program Derived Address, PDA), который принадлежит программе, а не конкретному пользователю. В блокчейне Solana у таких активов есть Metadata PDA и Master Edition PDA.

Взаимозаменяемые активы

Взаимозаменяемые активы — это токены SPL с ограниченным набором метаданных. К ним могут относиться такие токены, как USDC, а также токены сообществ или проектов. Токен соответствует стандарту Fungible, если при создании число его десятичных разрядов больше 0.

Взаимозаменяемый актив

Fungible Assets представляют объекты, а не отдельные единицы. Они могут содержать больше метаданных, чем стандартный Fungible-актив. Если при создании объекта стандарта Fungible число десятичных разрядов равно 0, он преобразуется в стандарт Fungible Asset.

Программируемые невзаимозаменяемые активы

Программируемые невзаимозаменяемые активы аналогичны стандарту Non-Fungible, но остаются в замороженном токен-аккаунте. В этом состоянии пользователи не могут сжигать, блокировать или передавать программируемые активы без взаимодействия с программой Token Metadata. Этот стандарт появился в ответ на дискуссию о роялти в Solana.

Доступные методы

DAS API предлагает различные методы для разных сценариев использования:

  1. getAsset: получить актив по его ID.
  2. searchAssets: найти активы по различным параметрам.
  3. getAssetProof: получить доказательство Merkle для сжатого актива по его ID.
  4. getAssetsByGroup: получить список активов по ключу и значению группы.
  5. getAssetsByOwner: получить список активов, принадлежащих определённому адресу.
  6. getAssetsByCreator: получить список активов, созданных определённым адресом.
  7. getAssetsByAuthority: найти список активов с определёнными полномочиями.

Подробные сведения о каждом методе доступны в нашей документации.

1. Получение актива

Эндпоинт getAsset позволяет получить конкретный актив по его ID. Этот ID может быть адресом токена в блокчейне или ID в дереве Merkle для сжатых активов.

Подробнее см. в документации по getAsset.

Пример

Предположим, мы хотим получить метаданные актива Claynosaurz с рейтингом 1. Для этого нужно найти ID актива, настроить функцию для обращения к DAS и обработать ответ, чтобы извлечь из результатов нужный объект.

Выполните следующие шаги:

  1. Сначала настройте функцию в новом файле с именем «getAsset.js»:
Код
const url = `https://rpc.helius.xyz/?api-key=`;

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

     2. Затем настройте операции fetch и await. Добавьте обязательный параметр ID в тело запроса:

Код
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',
      },
    }),
  });

На этом шаге мы отправляем POST-запрос к DAS API с уникальным ID актива в теле запроса.

    3. Обработайте результаты и выведите метаданные:

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

Этот фрагмент кода преобразует ответ API в формат JSON и выводит результат в консоль.

После этого скрипт можно запустить командой node getAsset.js.

Результат

После выполнения скрипт выведет метаданные указанного актива:

Код
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
}

Результат содержит подробные сведения об активе, включая тип интерфейса, ID, метаданные, создателей, полномочия и данные о владении.

Полный код

Ниже приведён полный код для получения актива по его 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();

Замените <api-key> своим фактическим ключом API.

В этом коде мы определяем асинхронную функцию getAsset, которая отправляет POST-запрос к DAS API. ID актива передаётся в теле запроса. После выполнения запроса функция преобразует ответ в JSON и выводит данные актива в консоль. В конце мы вызываем функцию getAsset, чтобы запустить этот процесс.

2. Получение доказательства актива

Эндпоинт getAssetProof используется для получения доказательства актива, необходимого для внесения изменений через программу сжатия. К таким изменениям относятся передача, сжигание, обновление создателя или коллекции и распаковка сжатых активов.

Подробный список изменений с использованием доказательства актива приведён в документации.

Пример

Чтобы получить доказательство актива, необходимое для изменения сжатого актива, выполните следующие шаги:

  1. Сначала настройте функцию в новом файле с именем «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. Затем настройте fetch и await. Добавьте обязательный параметр ID в тело запроса:

Код
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. Обработайте результат и извлеките корень:

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

‍Здесь мы извлекаем корень из доказательства актива. Его можно использовать для дальнейшего изменения сжатого актива.

Выполните node getAssetProof.js в терминале, чтобы получить данные указанного здесь актива.

Результат

Вывод будет содержать сведения о доказательстве актива:

Код
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'
}

Полный код

Ниже приведён полный код для получения доказательства актива по его 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();

Обязательно замените <api-key> своим фактическим ключом API.

В этом скрипте мы определяем асинхронную функцию getAssetProof, которая отправляет POST-запрос к Helius API. ID актива передаётся в теле запроса. После выполнения запроса функция преобразует ответ в JSON и выводит в консоль доказательство актива и его корень. В конце мы вызываем функцию getAssetProof, чтобы запустить этот процесс.

3. Поиск активов

Метод searchAssets получает цифровые активы по заданным параметрам поиска и позволяет гибко выбирать данные. Пользователи могут настраивать поиск и точно контролировать список возвращаемых активов.

Подробное описание параметров доступно в документации по searchAssets.

Пример

Рассмотрим пример, в котором нужно вывести изображение и название актива из кошелька пользователя. Актив должен принадлежать коллекции Drip Haus и быть сжатым. Для краткости мы запишем в JSON-файл названия и изображения всех активов Drip из кошелька в примере.

  1. Сначала создайте новый файл searchAssets.js со следующей асинхронной функцией:
Код
const fs = require('fs');
const url = `https://rpc.helius.xyz/?api-key=`;

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

     2. Реализуйте функцию, отправляющую POST-запрос с заданными параметрами поиска. Чтобы получить нужный результат, в данном случае мы используем параметры сжатия, адреса владельца и группировки по коллекции:

Код
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. Обработайте ответ, сгруппируйте активы по ID и устраните возможные дубликаты, указав точные данные, которые нужно извлечь из ответа для каждого объекта:

Код
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. Следующая функция находит повторы и удаляет их из списка. Если вы хотите показывать повторяющиеся активы, эту функцию можно не использовать. Если актив не является повтором, функция также добавляет его в новую группу:

Код
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. Сохраните результаты поиска в JSON-файл 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');

Чтобы выполнить скрипт, запустите node searchAssets.js. Результаты поиска будут записаны в файл searchResults.json.

Результат

Код
[
  {
    "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"
      }
    ]
  }, ...
}

Полный код

Код
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();

Обязательно замените <api-key> своим фактическим ключом API.

В этом скрипте мы определяем асинхронную функцию searchAssets, которая отправляет POST-запрос. В теле запроса используются различные параметры поиска, включая сжатие, адрес владельца и ID коллекции. После выполнения запроса функция преобразует ответ в JSON и извлекает нужные сведения об активах: ID, название и URI JSON. Она группирует активы по ID и сохраняет структурированные данные в JSON-файл searchResults.json. В конце мы вызываем функцию searchAssets, чтобы запустить этот процесс.

4. Получение активов по владельцу

Эндпоинт getAssetsByOwner возвращает список цифровых активов, принадлежащих определённому адресу. Сейчас это самый быстрый способ получить через Helius конкретные сведения о владельцах цифровых активов.

Пример

Чтобы получить активы, принадлежащие определённому адресу, выполните следующие шаги:

  1. Сначала настройте функцию в новом файле с именем «getAssetsByOwner.js»:
Код
const fs = require('fs');
const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetsByOwner = async () => {
  // Code goes here
};
getAssetsByOwner();
  1. Настройте fetch и await, затем добавьте параметры в тело запроса. Для этого эндпоинта мы используем 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. Затем обработайте ответ и извлеките сведения об активах, необходимые для записи в JSON. Мы используем ID актива, название и URI JSON, чтобы сгруппировать данные по искомому кошельку:
Код
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. Теперь можно настроить функцию для записи в 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');

Результат

Вывод будет содержать цифровые активы, принадлежащие указанному адресу:

Код
{
  "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"
      }, ...
}

Полный код

Ниже приведён полный код для получения активов, принадлежащих определённому адресу:

Код
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();

Обязательно замените <api-key> своим фактическим ключом API.

В этом скрипте мы определяем асинхронную функцию getAssetsByOwner, которая отправляет POST-запрос к Helius API. Адрес владельца передаётся в теле запроса. После выполнения запроса функция преобразует ответ в JSON и выводит в консоль активы, принадлежащие указанному адресу. В конце мы вызываем функцию getAssetsByOwner, чтобы запустить этот процесс.

5. Получение активов по группе

Эндпоинт getAssetsByGroup используется для получения цифровых активов, связанных с определённым ID коллекции. Этот эндпоинт необходим, если нужно получить объекты из конкретной коллекции или связать dApp с доступом по токену с определённой ончейн-коллекцией.

Пример

В этом сценарии мы создадим снимок коллекции SMB. Важно обработать ответ и извлечь владельца актива из объекта ownership, как описано в нашей документации. Цель — найти владельцев нескольких SMB и показать каждого только один раз в JSON, фактически создав «снимок».

  1. Сначала настройте функцию в новом файле с именем «getAssetsByGroup.js». Используйте Set, чтобы сохранять только уникальных владельцев:
Код
const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');
const uniqueOwners = new Set();

const getAssetsByGroup = async () => {
		 // Code goes here
};
getAssetsByGroup();
  1. Затем настройте fetch и await. Добавьте перечисленные выше параметры в тело запроса. Задайте в запросе groupKey и groupValue. Инициализируйте page значением 1, а hasMoreResults — значением true, чтобы настроить пагинацию. Позже функция пагинации вернёт false, если на странице будет меньше 1000 результатов — максимального числа на запрос:
Код
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. Обработайте результаты и извлеките строку владельца, необходимую для создания снимка текущих владельцев:
Код
const { result } = await response.json();
// Add each owner to the Set, automatically discarding duplicates
result.items.forEach(item => uniqueOwners.add(item.ownership.owner));
  1. Настройте пагинацию, увеличивая параметр page при наличии 1000 результатов. Если результатов меньше, задайте hasMoreResults значение false, чтобы остановить пагинацию:
Код
if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }
  1. Теперь можно преобразовать владельцев в массив и настроить корневое значение для записи в JSON количества держателей и каждого уникального кошелька владельца:
Код
const uniqueOwnersArray = Array.from(uniqueOwners);
  
  const root = {
    count: uniqueOwners.size,
    owners: uniqueOwnersArray
  };
  1. Преобразуйте Set с уникальными владельцами в массив. Настройте значение root для записи в JSON количества держателей и каждого уникального кошелька владельца:
Код
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);
};

Затем выполните команду node getAssetsByGroup.js, чтобы записать результаты в файл "ownerResults.json".

Результат

Результатом будет JSON-аналог снимка держателей с числом уникальных владельцев и их полным списком:

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

Полный код

Код
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();

Обязательно замените <api-key> своим фактическим ключом API.

Теперь вы умеете получать цифровые активы, связанные с определённым ID коллекции, через эндпоинт getAssetsByGroup. Эффективно используя пагинацию и структуру данных Set для сохранения уникальных владельцев, вы можете создать снимок текущих держателей любой коллекции с ончейн-ID.

6. Получение активов по создателю

Эндпоинт getAssetsByCreator используется для получения активов, созданных определённым адресом публичного ключа. Он полезен, если нужно найти в Solana активы, связанные с конкретным художником или проектом.

Пример

В этом примере мы получим активы, созданные Zen0. Можно использовать адрес создателя и задать параметру onlyVerified значение true, чтобы получить только активы, созданные подтверждённым кошельком. Мы обработаем результаты и выведем ID каждого актива и его владельца.

  1. Сначала настройте функцию в новом файле с именем "getAssetsByCreator.js". Для записи результатов в JSON-файл мы используем модуль fs:
Код
const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');

const getAssetsByCreator = async () => {
  // Code goes here
};
getAssetsByCreator();
  1. Теперь можно настроить запрос fetch и await ответ. Задайте в теле запроса необходимые параметры, включая creatorAddress и 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,
        },
      }),
    });

Не забудьте заменить <creator-address> фактическим адресом создателя, активы которого хотите получить.

  1. Обработайте ответ, извлеките результат и сохраните его в массиве:
Код
const { result } = await response.json();
allResults = allResults.concat(result.items);

if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }
  1. Сгруппируйте активы по ID владельца, поскольку один человек может владеть несколькими активами, и выведите активы каждого владельца. При обнаружении нового владельца в JSON-файле будет создана новая группа:
Код
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. Далее структурируйте результаты. В этом случае нас интересуют адрес NFT и владелец каждого возвращённого актива. С помощью root мы указываем размер результата и сохраняем изменённые данные для вывода:
Код
// 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. Наконец, сохраните результаты в файле "creatorResults.json" с помощью модуля fs:
Код
// 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');

Наконец, выполните команду node getAssetsByCreator.js, чтобы запустить скрипт и записать полученные результаты в файл "creatorResults.json".

Результат

Полученный JSON-файл содержит адрес публичного ключа каждого владельца и принадлежащие ему активы. Каждый актив представлен своим ID, что даёт наглядный снимок распределения активов, созданных определённым автором.

Код
{
  "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"
      }
    ]
  }, ...
}

'ownershipId' — это адрес публичного ключа владельца в блокчейне Solana, а assets — NFT, связанные с этим адресом.

Полный код

Код
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();

Обязательно замените <api-key> своим фактическим ключом API.

В этом примере мы отправляем асинхронный запрос к эндпоинту «getAssetsByCreator» в DAS API. Затем передаём адрес создателя и указываем, что он подтверждён. После этого настраиваем обработку ответа, чтобы извлечь владельца каждого актива, связанного с адресом создателя, и записать данные во внешний JSON-файл.

В целом это ценный инструмент для всех, кто хочет отслеживать или анализировать движение активов в блокчейне Solana, особенно связанных с конкретным создателем или проектом.

7. Получение активов по полномочиям

Функция getAssetsByAuthority получает активы, связанные с определёнными полномочиями на обновление. Полномочия на обновление принадлежат адресу, который имеет право изменять коллекцию. Эта функция особенно полезна, когда нужно получить набор активов, но ID коллекции отсутствует. Кроме того, она позволяет получать более широкий набор активов, связанных с адресом полномочий, не ограничиваясь одним ID коллекции.

Пример

В следующем примере мы получим все NFT коллекций Taiyo Robotics, Pilots и Infants вместе с их владельцами. Поскольку у них общие полномочия на обновление, можно вернуть все относящиеся к проекту результаты.

Это расширенная версия функции getAssetsByGroup, поскольку можно группировать активы, связанные с заданными полномочиями CDgbhX61QFADQAeeYKP5BQ7nnzDyMkkR3NEhYF2ETn1k.

  1. Сначала настройте функцию в новом файле с именем "getAssetsByAuthority.js":
Код
const url = `https://rpc.helius.xyz/?api-key=`;

const getAssetsByAuthority = async () => {
  // Code goes here
};
getAssetsByAuthority();
  1. Настройте запрос fetch и добавьте в тело необходимые параметры, такие как authorityAddress, page и 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,
        },
      }),
    });

Замените <authority-address> фактическим адресом полномочий, активы которого хотите извлечь.

     3. Теперь можно настроить циклическую обработку результатов, если их количество достигнет 1000.

     Если результатов меньше, флаг получит значение false и пагинация завершится: hasMoreResults вернёт false.

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

    if (result.items.length < 1000) {
      hasMoreResults = false;
    } else {
      page++;
    }
  }
  1. Обработайте активы, чтобы получить нужные сведения. Здесь мы хотим вывести ID актива и его владельца. Также создадим корневой объект с количеством активов и массивом обработанных активов:
Код
// 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. Теперь можно записать возвращённые объекты в JSON-файл authorityResults.json и вывести сообщение после завершения процесса.
Код
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");

Наконец, выполните команду node getAssetsByAuthority.js, чтобы запустить скрипт и записать полученные результаты в файл "authorityResults.json".

Результат

Вывод эндпоинта getAssetsByAuthority содержит количество активов и массив объектов. Каждый объект представляет актив и содержит его ID и владельца. Ниже приведён пример:

Код
{
  "count": 37330,
  "results": [
    {
      "id": "HmwL6uy7gQcXv74Mi8xF5G9mymZNd9rnUCpUFwSx1DX1",
      "owner": "5gt59Q14FEhT8LuETjLyiNHRkqCQf5hQsPP58ucBy5oC"
    },
    {
      "id": "HmztH56n4GD3p3EdgMKdzj8x21LjFQk4Bcp2YAQD8Urx",
      "owner": "H3AkHZHfcqGCcJpBn3FJWe52LcLxFMJQoZvZ6XyApFWf"
    },
    {
      "id": "Hn1NZXCaAxcr1btsaDm3eZRtLNbamy9FyrcVGLbZ2k5w",
      "owner": "AEqhqiQZBBa3dPTY1GwGsZJJnf5vzRKJpdXv6xzakfx8"
    }, ...
}

Полный код

Код
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()

Замените <api-key> своим фактическим ключом API.

В этом примере мы используем метод "getAssetsByAuthority", чтобы вернуть все активы из трёх коллекций, связанные с одним адресом полномочий. В качестве дополнительных параметров запроса используются только page и limit. Затем мы обрабатываем результаты, извлекаем активы, связанные с этими полномочиями, и сохраняем их во внешнем JSON-файле.

Заключение

Теперь вы должны хорошо понимать, как использовать API стандарта цифровых активов Digital Asset Standard (DAS), и знать несколько практических сценариев для каждого метода. Это важное отличие от прежнего подхода, при котором для получения конкретных сведений об активах требовалось несколько эндпоинтов.

Эти методы работают как со сжатыми, так и с обычными активами и предлагают единый интерфейс для разных вариантов запросов.

Дополнительные сведения об API стандарта цифровых активов Digital Asset Standard (DAS) доступны в нашей подробной документации. Также изучите наш виджет Open API с детальным описанием параметров запросов и ответов.

Как всегда, приглашаем вас присоединиться к нашему сообществу в Discord. Если у вас появятся вопросы, смело пишите мне там!

‍

Подпишитесь на Helius

Следите за новостями разработки на Solana и получайте уведомления о наших публикациях