{"author":{"address":null,"user":"https://learnblockchain.cn/people/23919"},"content":{"body":"\u003c!--StartFragment--\u003e\r\n\r\n## 概述\r\n\r\n**数字资产标准**( DAS) API 是一个新发布的接口，它统一了 Solana 上的常规资产和压缩资产（代币、NFT 等）。随着压缩资产的引入，Solana 开发人员现在可以更有效地检索与钱包、集合或授权相关的所有资产，而无需使用多个端点。DAS API 也在后台编制索引，从而为您作为开发人员提供最高效的调用。有了 DAS，您可以通过消除冗长的 gPA 调用来简化检索信息的过程。在 getAssetsByOwner 端点中，您可以使用其链上集合 ID 访问属于特定集合的所有资产的元数据和链下信息。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n## 先决条件\r\n\r\n* [已安装Node.js]（使用内置提取功能需 v18.0 或更高版本）。\r\n* 对 JavaScript 有基本的了解。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n### 设置环境\r\n\r\n1. 为该项目创建一个名为 collection 的文件夹。\r\n2. 在**collection 文件夹**中，创建一个名为**assetList.js**的文件。我们将在此文件中编写我们的函数。\r\n3. [**在我们的开发者门户**]上创建 API 密钥。导航到 RPC 并复制主网 RPC 链接，该链接将在本教程中用作 URL 变量。\r\n4. 获取用于测试的演示集合的认证集合 ID。在本例中，我们将使用 Mad Lads，其集合 ID 为**J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w**。在查看特定 NFT 时，您可以在 Magic Eden 等市场上找到链上集合地址。\r\n\u003c!--EndFragment--\u003e\r\n\r\n![67322d0b4e7f1b12213cf5d8_6474f2a34728165b9495b109_das1.png](https://img.learnblockchain.cn/attachments/2024/12/kb5c7eZ0676d3749e0b0c.png)\r\n\u003c!--StartFragment--\u003e\r\n*请注意，如果没有链上收集 ID，您将需要使用其他 DAS 方法来检索结果。*\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n## 遵循的步骤\r\n\r\n##### 1.创建 getAssetsByGroup 函数\r\n\r\n首先，让我们创建一个函数来检索与集合相关的所有资产。我们将在此函数中嵌套对 DAS API 的 POST 请求。\r\n\r\n首先建立一个异步函数：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\nconst { promises : fs } = require(\"fs\");\r\nconst url = `https://rpc.helius.xyz/?api-key=`;\r\n\r\nconst getAssetsByGroup = async () =\u003e {\r\n// Code goes here. \r\n};\r\n\r\ngetAssetsByGroup();\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n在本节中，我们导入了用于处理文件系统操作的**fs**模块，识别了 RPC URL，并声明了**getAssetsByGroup**函数。\r\n\r\n确保使用来自[开发者门户]的 API 密钥替换**\\\u003capi-key\u003e**。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n##### 2. 创建对 DAS 的 POST 请求\r\n\r\n让我们定义**getAssetsByGroup**函数并指定起始页面和请求返回参数。我们将使用 fetch 函数来与我们的[**方法文档**]**保持一致。**\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\nconsole.time('getAssetsByGroup');\r\nlet page = 1;\r\nlet assetList = [];\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n**我们使用console.time('getAssetsByGroup')**启动一个计时器，并为当前页面初始化变量和一个空数组来存储获取的资产。\r\n\r\n我们使用**fetch**和**await**向指定的 url 端点发送异步 POST 请求：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\ntry {\r\n   while (page) {\r\n    const response = await fetch(url, {\r\n      method: 'POST',\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n接下来，我们进入一个**while**循环，只要**页面**变量不为假，它就会继续从 API 获取数据。\r\n\r\n然后，我们将**fetch**与**await**结合使用，这是一个用于发送 HTTP 请求的异步操作。我们指定API 端点的**URL**，并将方法设置为“POST”。这意味着我们在请求正文中将数据发送到服务器。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\nheaders: {\r\n\t'Content-Type': 'application/json',\r\n},\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n在请求的标头中，我们将“Content-Type”设置为“application/json”。这告诉服务器我们正在发送 JSON 数据。\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\nbody: JSON.stringify({\r\n\tjsonrpc: '2.0',\r\n\tid: 'my-id',\r\n\tmethod: 'getAssetsByGroup',\r\n\tparams: {\r\n\t\tgroupKey: 'collection',\r\n\t\tgroupValue: 'J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w',\r\n\t\tpage: page,\r\n\t\tlimit: 1000,\r\n\t},\r\n}),\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n接下来，我们配置请求的主体，即一个 JSON 对象，我们将其字符串化为可以发送到端点的格式。在这里我们定义 groupKey（这将是“集合”）和 groupValue（这将代表链上集合 ID）。\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\n\r\nif (!response.ok) {\r\n        throw new Error(`HTTP error! status: ${response.status}`);\r\n    }\r\n\r\nconst { result } = await response.json();   \r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n现在，如果服务器响应不肯定，我们将触发一个错误来捕获。如果请求成功，它将以 JSON 格式呈现响应。\r\n\r\n如果您没有在 URL 中设置有效的 API 密钥，则可能会遇到错误。\r\n\r\n##### **3. 将新资产添加到列表中**\r\n\r\n在上一节中，我们最初指示**getAssetsByGroup**在页面设置为 1 时运行。但是，它尚未配置为遍历所有可能的结果页面。接下来让我们确定这一点：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\nassetList.push(...result.items);\r\n    if (result.total !== 1000) {\r\n      page = false;\r\n    } else {\r\n      page++;\r\n    }\r\n  }\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n此代码将响应中的项目添加到**assetList**数组。如果结果总数不等于 1,000 的限制，我们将**page**设置为**false**以退出循环。\r\n\r\n##### **4. 将资产记录到文件中**\r\n\r\n要将检索到的资产信息保存到外部 JSON 文件，请添加以下代码：\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\nconst resultData = {\r\n    totalResults: assetList.length,\r\n    results: assetList,\r\n  };\r\n\r\n  await fs.writeFile('results.json', JSON.stringify(resultData, null, 2));\r\n\tconsole.log('Results saved to results.json')\r\n  console.timeEnd('getAssetsByGroup');\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n此代码构建了一个**resultData**对象，该对象由总结果数和**assetList**数组组成。我们应用**fs.writeFile**将数据写入名为**results.json的 JSON 文件。最后，我们记录一条确认消息并使用****console.timeEnd**结束计时器。\r\n\r\n##### **5. 实现错误处理**\r\n\r\n现在，我们需要设计一个安全网来解决潜在的服务器请求失败。这可以通过以下设置来实现：\r\n\r\n如果在执行请求期间出现问题，此代码块将在我们的控制台中记录一条错误消息。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\n} catch (error) {\r\n    console.error('Error occurred:', error);\r\n}\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n如果您没有插入有效的链上收集 ID，您可能会遇到请求错误。\r\n\r\n#### 最终代码\r\n\r\n您的**assetList.js**文件应类似于以下代码片段。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\n\r\nconst { promises : fs } = require(\"fs\");\r\nconst url = `https://rpc.helius.xyz/?api-key=`;\r\n\r\nconst getAssetsByGroup = async () =\u003e {\r\n  console.time('getAssetsByGroup'); // Start the timer\r\n  let page = 1;\r\n  let assetList = [];\r\n\r\ntry {\r\n   while (page) {\r\n    const response = await fetch(url, {\r\n      method: 'POST',\r\n      headers: {\r\n        'Content-Type': 'application/json',\r\n      },\r\n      body: JSON.stringify({\r\n        jsonrpc: '2.0',\r\n        id: 'my-id',\r\n        method: 'getAssetsByGroup',\r\n        params: {\r\n          groupKey: 'collection',\r\n          groupValue: 'J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w',\r\n          page: page,\r\n          limit: 1000,\r\n        },\r\n      }),\r\n    });\r\n\t\tif (!response.ok) {\r\n        throw new Error(`HTTP error! status: ${response.status}`);\r\n    }\r\n    const { result } = await response.json();\r\n\r\n    assetList.push(...result.items);\r\n    if (result.total !== 1000) {\r\n      page = false;\r\n    } else {\r\n      page++;\r\n    }\r\n  }\r\n\r\n  const resultData = {\r\n    totalResults: assetList.length,\r\n    results: assetList,\r\n  };\r\n\r\n  await fs.writeFile('results.json', JSON.stringify(resultData, null, 2));\r\n\tconsole.log('Results saved to results.json')\r\n  console.timeEnd('getAssetsByGroup'); \r\n\r\n\t} catch (error) {\r\n    console.error('Error occurred:', error);\r\n  }\r\n\r\n};\r\n\r\ngetAssetsByGroup();\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n### 结果\r\n\r\n一旦您的文件类似于上面的代码，您就可以使用终端中的**node assetList.js**命令运行它来启动请求。这将生成一个**results.json**文件。\r\n\r\n完成后，控制台将显示结果已保存到**results.json**文件，并记录获取资产所需的时间。在我们的案例中，使用 Node.js 检索 Mad Lads 链上收藏的资产信息，该过程平均花费**9.27 秒**。\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n![67322d0b4e7f1b12213cf5d8_6474f2a34728165b9495b109_das1.png](https://img.learnblockchain.cn/attachments/2024/12/PysO5JrN676d38bd29f98.png)\r\n\u003c!--StartFragment--\u003e\r\n\r\n打开**results.json**文件后，您将看到返回的结果总数以及资产详细信息。这些将代表属于您查询的集合的各个 NFT。\r\n\r\n为了进一步定制返回的数据，您可以提取特定信息，例如图像、所有者和其他被视为有价值的元数据。\r\n\r\n*请注意，由于已销毁资产和链下资产的计算，该集合可能显示总数为 9967，而不是 10,000*\r\n\r\n#### 结果.json\r\n\r\n\u003c!--EndFragment--\u003e\r\n\r\n```js\r\n\r\n{\r\n  \"totalResults\": 9967,\r\n  \"results\": [\r\n    {\r\n      \"interface\": \"Custom\",\r\n      \"id\": \"GVPX9rXRXo9SVGktJCzA3Qb9v263kQzEyAWsgX3LL8P5\",\r\n      \"content\": {\r\n        \"$schema\": \"https://schema.metaplex.com/nft1.0.json\",\r\n        \"json_uri\": \"https://madlads.s3.us-west-2.amazonaws.com/json/859.json\",\r\n        \"files\": [\r\n          {\r\n            \"uri\": \"https://madlads.s3.us-west-2.amazonaws.com/images/859.png\",\r\n            \"cdn_uri\": \"https://cdn.helius.services/cdn-cgi/image//https://madlads.s3.us-west-2.amazonaws.com/images/859.png\",\r\n            \"mime\": \"image/png\"\r\n          },\r\n          {\r\n            \"uri\": \"https://arweave.net/qJ5B6fx5hEt4P7XbicbJQRyTcbyLaV-OQNA1KjzdqOQ/859.png\",\r\n            \"cdn_uri\": \"https://cdn.helius.services/cdn-cgi/image//https://arweave.net/qJ5B6fx5hEt4P7XbicbJQRyTcbyLaV-OQNA1KjzdqOQ/859.png\",\r\n            \"mime\": \"image/png\"\r\n          }\r\n        ],\r\n        \"metadata\": {\r\n          \"attributes\": [\r\n            {\r\n              \"value\": \"Male\",\r\n              \"trait_type\": \"Gender\"\r\n            },\r\n            {\r\n              \"value\": \"Galaxy\",\r\n              \"trait_type\": \"Type\"\r\n            },\r\n            {\r\n              \"value\": \"Galaxy\",\r\n              \"trait_type\": \"Expression\"\r\n            },\r\n            {\r\n              \"value\": \"Gambler\",\r\n              \"trait_type\": \"Hat\"\r\n            },\r\n            {\r\n              \"value\": \"Galaxy\",\r\n              \"trait_type\": \"Eyes\"\r\n            },\r\n            {\r\n              \"value\": \"Dark Windsor\",\r\n              \"trait_type\": \"Clothing\"\r\n            },\r\n            {\r\n              \"value\": \"Grey\",\r\n              \"trait_type\": \"Background\"\r\n            }\r\n          ],\r\n          \"description\": \"Fock it.\",\r\n          \"name\": \"Mad Lads #859\",\r\n          \"symbol\": \"MAD\"\r\n        },\r\n        \"links\": {\r\n          \"external_url\": null\r\n        }\r\n      },\r\n      \"authorities\": [\r\n        {\r\n          \"address\": \"2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW\",\r\n          \"scopes\": [\r\n            \"full\"\r\n          ]\r\n        }\r\n      ],\r\n      \"compression\": {\r\n        \"eligible\": false,\r\n        \"compressed\": false,\r\n        \"data_hash\": \"\",\r\n        \"creator_hash\": \"\",\r\n        \"asset_hash\": \"\",\r\n        \"tree\": \"\",\r\n        \"seq\": 0,\r\n        \"leaf_id\": 0\r\n      },\r\n      \"grouping\": [\r\n        {\r\n          \"group_key\": \"collection\",\r\n          \"group_value\": \"J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w\"\r\n        }\r\n      ],\r\n      \"royalty\": {\r\n        \"royalty_model\": \"creators\",\r\n        \"target\": null,\r\n        \"percent\": 0.042,\r\n        \"basis_points\": 420,\r\n        \"primary_sale_happened\": true,\r\n        \"locked\": false\r\n      },\r\n      \"creators\": [\r\n        {\r\n          \"address\": \"5XvhfmRjwXkGp3jHGmaKpqeerNYjkuZZBYLVQYdeVcRv\",\r\n          \"share\": 0,\r\n          \"verified\": true\r\n        },\r\n        {\r\n          \"address\": \"2RtGg6fsFiiF1EQzHqbd66AhW7R5bWeQGpTbv2UMkCdW\",\r\n          \"share\": 100,\r\n          \"verified\": true\r\n        }\r\n      ],\r\n      \"ownership\": {\r\n        \"frozen\": false,\r\n        \"delegated\": false,\r\n        \"delegate\": null,\r\n        \"ownership_model\": \"single\",\r\n        \"owner\": \"GX6KFMFS6yZGJzuZ28Q5Cbk9RN8Wv8UmNP2abcC4kcM2\"\r\n      },\r\n      \"supply\": null,\r\n      \"mutable\": true\r\n    }, ... \r\n    // Addtional Items\r\n]\r\n```\r\n\u003c!--StartFragment--\u003e\r\n\r\n这将显示返回的全部资产。现在，您可以进一步细分，仅返回代币地址、所有者和其他各种元数据信息。\r\n\r\n*您会注意到，收藏量显示为 9967，而不是 10,000。这是因为反映了已销毁的数量，而不再在链上。*\r\n\r\n\u003c!--EndFragment--\u003e\r\n\u003c!--StartFragment--\u003e\r\n\r\n## 结论\r\n\r\n恭喜！您已使用新发布的数字资产标准 (DAS) API 成功检索了 10k 大小的集合中的所有资产。总结如下：\r\n\r\n* DAS API 为 Solana dApps 提供了一种简化的资产获取方法。\r\n* 该方法适用于常规集合和压缩集合。\r\n* 通过利用 DAS API，您可以在不到 15 秒的时间内访问有价值的元数据和所有权信息。\r\n\r\n通过使用 DAS API，我们可以简化 Solana 上 dApp 的资产获取。我们只需使用一个端点，而不必进行多次 API 调用来收集信息。\r\n\r\n\u003c!--EndFragment--\u003e\r\n作者：https://t.me/+XB-mhZfcnQExOTdl\r\n来源：https://www.fabipingtai.com","title":"Solana Dev 101 - 使用 DAS API 获取集合中的所有 NFT"},"history":null,"timestamp":1735211290,"version":1}