
Solana의 새로운 DAS API에 대해 알아야 할 모든 것
개요
Digital Asset Standard(DAS) API의 도입으로 이제 Solana에서 NFT와 토큰을 더 간편하게 조회할 수 있습니다. Solana 개발자 도구에 새롭게 추가된 DAS API는 Solana의 디지털 자산을 조회하는 통합 인터페이스를 제공합니다. 개발자는 자산 유형별로 여러 엔드포인트를 사용할 필요 없이 단일 API를 통해 애플리케이션에 필요한 데이터를 가져올 수 있습니다.
이 대화형 가이드에서 다룰 내용은 다음과 같습니다.
- Solana에서 사용할 수 있는 자산 유형의 이해
- DAS API가 제공하는 메서드에 대한 종합적인 설명
- 쉽게 맞춤 설정할 수 있는 각 엔드포인트의 실제 사용 사례
이 가이드의 각 사용 사례를 따라 하면 마지막에는 DAS를 능숙하게 활용할 수 있습니다.
사전 요구 사항
- Node.js 설치(내장 fetch를 사용하려면 v18.0 필요)
- Helius RPC
- JavaScript 기본 지식
환경 설정
- functions라는 프로젝트 폴더를 만드세요.
- 각 예제마다 이 폴더 안에 새 파일을 만드세요.
자산 유형
Solana 생태계에서 "자산"은 블록체인에 존재하는 토큰이나 대체 불가능 토큰(NFT)과 같이 가치를 지닌 모든 디지털 항목을 의미합니다. Solana는 다양한 자산을 지원합니다. 이러한 자산과 상호작용할 때 DAS API가 반환하는 데이터 유형을 이해하는 것이 중요합니다.
각 자산 유형을 자세히 살펴보겠습니다.
대체 불가능
대체 불가능 자산은 표준 NFT 모델을 따르며 메타데이터를 토큰 계정에 저장합니다. 이 데이터는 특정 사용자가 아닌 프로그램이 소유하는 주소인 Program Derived Address(PDA)에 존재합니다. Solana 블록체인에서 Metadata PDA와 Master Edition PDA를 사용합니다.
대체 가능
대체 가능 자산은 제한된 메타데이터를 가진 SPL 토큰입니다. USDC 같은 토큰이나 커뮤니티 또는 프로젝트 토큰을 나타낼 수 있습니다. 생성 시 소수점 자릿수가 0보다 크면 해당 토큰은 Fungible 표준을 따릅니다.
대체 가능 자산
Fungible Asset은 개별 단위가 아닌 항목을 나타냅니다. 표준 Fungible 자산보다 더 많은 메타데이터를 포함할 수 있습니다. Fungible 표준 항목을 생성할 때 소수점 자릿수를 0으로 설정하면 Fungible Asset 표준으로 전환됩니다.
프로그래밍 가능한 대체 불가능 자산
프로그래밍 가능한 대체 불가능 자산은 Non-Fungible 표준과 유사하지만 동결된 토큰 계정에 유지됩니다. 이 상태에서는 Token Metadata 프로그램과 상호작용하지 않고 프로그래밍 가능한 자산을 소각하거나 잠그거나 전송할 수 없습니다. 이 표준은 Solana의 로열티 논쟁에 대응해 만들어졌습니다.
사용 가능한 메서드
DAS API는 다양한 사용 사례에 맞춘 여러 메서드를 제공합니다.
getAsset: ID로 자산을 조회합니다.searchAssets: 다양한 매개변수로 자산을 찾습니다.getAssetProof: ID로 압축 자산의 머클 증명을 가져옵니다.getAssetsByGroup: 그룹 키와 값으로 자산 목록을 가져옵니다.getAssetsByOwner: 특정 주소가 소유한 자산 목록을 조회합니다.getAssetsByCreator: 특정 주소가 생성한 자산 목록을 가져옵니다.getAssetsByAuthority: 특정 권한을 가진 자산 목록을 찾습니다.
각 메서드에 대한 자세한 내용은 문서를 참고하세요.
1. 자산 가져오기
getAsset 엔드포인트를 사용하면 ID로 특정 자산을 조회할 수 있습니다. 이 ID는 온체인 토큰 주소이거나 압축 자산의 머클 트리 ID일 수 있습니다.
자세한 내용은 getAsset 문서를 참고하세요.
예제
순위 1위 Claynosaurz 자산의 메타데이터를 가져온다고 가정해 보겠습니다. 먼저 자산 ID를 찾고 DAS를 호출할 함수를 설정한 다음, 결과에서 필요한 객체를 정확히 파싱하도록 응답을 구성해야 합니다.
아래 단계를 따르세요.
- 먼저 “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',
},
}),
});이 단계에서는 요청 본문에 자산의 고유 ID를 담아 DAS API에 POST 요청을 보냅니다.
3. 결과를 파싱하고 메타데이터 정보를 표시하세요.
const { result } = await response.json();
console.log("asset: ", result);이 코드 조각은 API 응답을 JSON 형식으로 파싱하고 결과를 콘솔에 기록합니다.
그런 다음 node getAsset.js 명령어로 스크립트를 실행할 수 있습니다.
이 메서드는 단일 자산의 데이터를 조회합니다. 자산 그룹을 검색해야 한다면 DAS API가 제공하는 추가 메서드를 사용할 수 있습니다. 이에 대해서는 뒤에서 설명합니다.
결과
이 스크립트를 실행하면 지정한 자산의 메타데이터가 표시됩니다.
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 키로 바꾸세요.
이 코드에서는 DAS API에 POST 요청을 보내는 비동기 함수 getAsset을 정의합니다. 요청 본문에 자산 ID를 전달합니다. 요청이 완료되면 함수가 응답을 JSON으로 파싱하고 자산 데이터를 콘솔에 출력합니다. 마지막으로 getAsset 함수를 호출해 이 과정을 실행합니다.
2. 자산 증명 가져오기
getAssetProof 엔드포인트는 압축 프로그램을 수정하는 데 필요한 자산 증명을 조회합니다. 이러한 수정에는 압축 자산의 전송, 소각, 생성자 업데이트, 컬렉션 업데이트, 압축 해제 등이 포함됩니다.
자산 증명을 사용하는 수정 작업의 자세한 목록은 문서를 참고하세요.
예제
압축 자산을 수정하는 데 필요한 자산 증명을 가져오려면 다음 단계를 따르세요.
- 먼저 “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 키로 바꾸세요.
이 스크립트에서는 Helius API에 POST 요청을 보내는 비동기 함수 getAssetProof을 정의합니다. 요청 본문에 자산 ID를 전달합니다. 요청이 완료되면 함수가 응답을 JSON으로 파싱하고 자산 증명과 루트를 콘솔에 출력합니다. 마지막으로 getAssetProof 함수를 호출해 이 과정을 실행합니다.
3. 자산 검색
searchAssets 메서드는 지정한 검색 매개변수에 따라 디지털 자산을 조회하므로 데이터를 유연하게 가져올 수 있습니다. 검색 조건을 맞춤 설정해 반환되는 자산을 더 세밀하게 제어할 수 있습니다.
자세한 매개변수는 searchAssets 문서에서 확인할 수 있습니다.
예제
사용자 지갑에서 Drip Haus 컬렉션에 속한 자산의 이미지와 이름을 표시하되, 그중 압축된 항목만 표시하는 예제를 살펴보겠습니다. 이 튜토리얼에서는 간결성을 위해 예제 지갑에 있는 각 Drip 자산의 이름과 이미지를 JSON 파일로 출력합니다.
- 먼저 다음 비동기 함수가 포함된 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. 검색 결과를 searchResults.json이라는 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 키로 바꾸세요.
이 스크립트에서는 POST 요청을 보내는 비동기 함수 searchAssets을 정의합니다. 요청 본문에는 압축 여부, 소유자 주소, 컬렉션 ID 등 다양한 검색 매개변수를 사용합니다. 요청이 완료되면 함수가 응답을 JSON으로 파싱하고 관련 자산 정보(ID, 이름, JSON URI)를 추출합니다. 이 자산을 ID별로 그룹화하고 정리된 데이터를 searchResults.json이라는 JSON 파일에 저장합니다. 마지막으로 searchAssets 함수를 호출해 이 과정을 시작합니다.
4. 소유자별 자산 가져오기
getAssetsByOwner 엔드포인트는 특정 주소가 소유한 디지털 자산 목록을 제공합니다. 현재 Helius를 사용해 디지털 자산의 특정 소유권 정보를 조회하는 가장 빠른 방법입니다.
예제
특정 주소가 소유한 자산을 가져오려면 다음 단계를 따르세요.
- 먼저 “getAssetsByOwner.js”라는 새 파일에 함수를 설정하세요.
const fs = require('fs');
const url = `https://rpc.helius.xyz/?api-key=`;
const getAssetsByOwner = async () => {
// Code goes here
};
getAssetsByOwner();- 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
},
}),
});- 그런 다음 응답을 파싱하고 JSON에 출력할 자산 정보를 추출하세요. 자산 ID, 이름, json URI를 사용해 검색 중인 지갑별로 그룹화합니다.
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],
};
}
}- 이제 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 키로 바꾸세요.
이 스크립트에서는 Helius API에 POST 요청을 보내는 비동기 함수 getAssetsByOwner을 정의합니다. 요청 본문에 소유자 주소를 전달합니다. 요청이 완료되면 함수가 응답을 JSON으로 파싱하고 지정한 주소가 소유한 자산을 콘솔에 출력합니다. 마지막으로 getAssetsByOwner 함수를 호출해 이 과정을 실행합니다.
5. 그룹별 자산 가져오기
getAssetsByGroup 엔드포인트는 특정 컬렉션 ID와 연결된 디지털 자산을 조회합니다. 특정 컬렉션의 항목을 가져오거나 토큰 게이팅 dApp을 특정 온체인 컬렉션과 연결해야 할 때 중요한 엔드포인트입니다.
예제
이 시나리오에서는 SMB 컬렉션의 스냅샷을 설정합니다. 문서에 설명된 대로, 응답을 파싱해 소유권 객체에서 자산 소유자를 추출하는 것이 중요합니다. 여러 SMB를 가진 소유자를 필터링하고 JSON에는 한 번만 표시해 효과적으로 “스냅샷”을 만드는 것이 목표입니다.
- 먼저 “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();- fetch와 await를 설정하세요. 위에 나열된 매개변수를 요청 본문에 지정하세요. 요청에 groupKey와 groupValue를 설정하세요. 페이지네이션을 준비하려면
page을 1로,hasMoreResults을 true로 초기화하세요. 이후 페이지 결과가 1,000개(요청당 최대 한도)보다 적으면 false를 반환하는 페이지네이션 함수를 설정합니다.
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));- 결과가 1,000개라면
page매개변수를 늘려 페이지네이션을 설정하세요. 더 적다면hasMoreResults을 false로 설정해 페이지네이션을 중지하세요.
if (result.items.length < 1000) {
hasMoreResults = false;
} else {
page++;
}
}- 이제 소유자를 배열로 설정하고, 보유자 수와 고유한 각 소유자 지갑을 JSON에 출력하도록 루트 값을 설정할 수 있습니다.
const uniqueOwnersArray = Array.from(uniqueOwners);
const root = {
count: uniqueOwners.size,
owners: uniqueOwnersArray
};- 고유 소유자의
Set을 배열로 변환하세요. 보유자 수와 고유한 각 소유자 지갑을 JSON에 출력하도록root값을 설정하세요.
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 키로 바꾸세요.
이제 getAssetsByGroup 엔드포인트를 사용해 특정 컬렉션 ID와 연결된 디지털 자산을 조회하는 방법을 배웠습니다. 페이지네이션과 Set 데이터 구조를 효과적으로 사용해 고유한 소유자 항목만 유지하면 온체인 ID가 있는 모든 컬렉션의 현재 보유자 스냅샷을 만들 수 있습니다.
6. 생성자별 자산 가져오기
getAssetsByCreator 엔드포인트는 특정 공개 키 주소가 생성한 자산을 조회합니다. Solana에서 특정 아티스트나 프로젝트와 관련된 자산을 찾을 때 유용합니다.
예제
이 예제에서는 Zen0이 생성한 자산을 반환합니다. 생성자 주소를 사용하고 onlyVerified 매개변수를 true로 설정하면 검증된 지갑이 생성한 자산만 조회할 수 있습니다. 결과를 파싱해 각 자산의 ID와 소유자를 표시합니다.
- 먼저 "getAssetsByCreator.js"라는 새 파일에 함수를 설정하세요. 결과를 JSON 파일에 출력하기 위해
fs모듈을 사용합니다.
const url = `https://rpc.helius.xyz/?api-key=`;
const fs = require('fs');
const getAssetsByCreator = async () => {
// Code goes here
};
getAssetsByCreator();- 이제 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>을 자산을 조회하려는 생성자의 실제 주소로 바꾸세요.
- 응답을 파싱해 결과를 추출하고 배열에 저장하세요.
const { result } = await response.json();
allResults = allResults.concat(result.items);
if (result.items.length < 1000) {
hasMoreResults = false;
} else {
page++;
}
}- 한 사람이 여러 자산을 소유할 수 있으므로 소유권 ID를 기준으로 자산을 그룹화하고 각 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],
};
}
}- 다음 단계는 결과를 구조화하는 것입니다. 여기서는 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
};- 마지막으로
fs모듈을 사용해 결과를 "creatorResults.json"이라는 파일에 저장하세요.
// 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의 범위를 넘어 권한 주소와 연결된 더 많은 자산을 조회할 수 있습니다.
예제
다음 예제에서는 Taiyo Robotics, Pilots, Infants의 각 컬렉션 NFT와 해당 소유자를 조회합니다. 모두 동일한 업데이트 권한을 공유하므로 프로젝트와 관련된 모든 결과를 반환할 수 있습니다.
설정된 권한 CDgbhX61QFADQAeeYKP5BQ7nnzDyMkkR3NEhYF2ETn1k에 연결된 자산을 그룹화할 수 있으므로 getAssetsByGroup 함수보다 향상된 방식입니다.
- 먼저 새로 만든 "getAssetsByAuthority.js" 파일에 함수를 설정하세요.
const url = `https://rpc.helius.xyz/?api-key=`;
const getAssetsByAuthority = async () => {
// Code goes here
};
getAssetsByAuthority();- 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. 이제 결과가 1,000개인 경우 결과를 순회하도록 반환값을 구성할 수 있습니다.
결과 수가 더 적으면 플래그가 false로 설정되고 페이지네이션이 종료되며 hasMoreResults가 false를 반환합니다.
const { result } = await response.json();
totalResults.push(...result.items);
if (result.items.length < 1000) {
hasMoreResults = false;
} else {
page++;
}
}- 자산을 처리해 필요한 정보를 추출하세요. 여기서는 자산 ID와 소유자를 표시합니다. 또한 자산 수와 처리된 자산 배열을 담는 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
};- 이제 반환된 항목을 authorityResults.json이라는 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" 메서드를 사용해 3개 컬렉션에 걸쳐 권한 주소와 연결된 모든 자산을 반환합니다. 요청의 추가 매개변수로 page과 limit만 사용합니다. 그런 다음 결과를 파싱해 해당 권한 아래의 자산을 추출하고 외부 JSON 파일에 저장합니다.
결론
이제 Digital Asset Standard(DAS) API의 사용법과 각 메서드의 여러 실용적인 활용 사례를 종합적으로 이해했을 것입니다. 특정 자산 정보를 가져오기 위해 여러 엔드포인트를 사용해야 했던 기존 방식과 크게 달라졌습니다.
이러한 메서드는 압축 자산과 일반 자산을 모두 지원하며, 다양한 방식으로 요청할 수 있는 통합 인터페이스를 제공합니다.
자세한 내용은 Digital Asset Standard(DAS) API에 관한 광범위한 문서를 참고하세요. 요청 및 응답 매개변수를 자세히 설명하는 Open API 위젯도 살펴볼 수 있습니다.
언제나처럼 Discord 커뮤니티 참여를 환영합니다. 궁금한 점이 있다면 언제든 그곳에서 저를 호출하세요!
관련 아티클
Helius 구독하기
최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요


