Skip to content

Commit 87e2e35

Browse files
Merge pull request #205 from metaplex-foundation/mark/fetchAllAssets
new helper fetchAllAssets
2 parents 30fbdf1 + a4eccbf commit 87e2e35

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

clients/js/src/helpers/fetch.ts

+60
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import {
99
AssetV1,
1010
CollectionV1,
11+
fetchAllAssetV1,
1112
fetchAllCollectionV1,
1213
fetchAssetV1,
1314
fetchCollectionV1,
@@ -147,6 +148,65 @@ export const fetchAsset = async (
147148
return deriveAssetPlugins(assetV1, await fetchCollectionV1(umi, collection));
148149
};
149150

151+
/**
152+
* Helper function to fetch multiple assets and derive plugins from their collections if applicable.
153+
*
154+
* @param umi Context
155+
* @param assets Array of asset addresses to fetch
156+
* @param options Options, `skipDerivePlugins` plugins from collection is false by default; `chunksize` how many assets to fetch in a single rpc call.
157+
* @returns Promise of a list of `AssetV1`
158+
*/
159+
export const fetchAllAssets = async (
160+
umi: Context,
161+
assets: Array<PublicKey | string>,
162+
options: {
163+
skipDerivePlugins?: boolean;
164+
chunkSize?: number;
165+
} & RpcGetAccountOptions = {}
166+
): Promise<AssetV1[]> => {
167+
const chunkSize = options.chunkSize ?? 1000;
168+
const assetChunks = [];
169+
for (let i = 0; i < assets.length; i += chunkSize) {
170+
assetChunks.push(assets.slice(i, i + chunkSize));
171+
}
172+
173+
const assetV1s = (
174+
await Promise.all(
175+
assetChunks.map((chunk) =>
176+
fetchAllAssetV1(
177+
umi,
178+
chunk.map((asset) => publicKey(asset))
179+
)
180+
)
181+
)
182+
).flat();
183+
184+
if (options.skipDerivePlugins) {
185+
return assetV1s;
186+
}
187+
188+
const collectionKeys = Array.from(
189+
new Set(assetV1s.map((asset) => collectionAddress(asset)))
190+
).filter((collection): collection is PublicKey => !!collection);
191+
192+
const collections = await fetchAllCollectionV1(umi, collectionKeys);
193+
const collectionMap = collections.reduce(
194+
(map, collection) => {
195+
map[collection.publicKey] = collection;
196+
return map;
197+
},
198+
{} as { [key: string]: CollectionV1 }
199+
);
200+
201+
return assetV1s.map((assetV1) => {
202+
const collection = collectionAddress(assetV1);
203+
if (!collection) {
204+
return assetV1;
205+
}
206+
return deriveAssetPlugins(assetV1, collectionMap[collection]);
207+
});
208+
};
209+
150210
/**
151211
* Helper function to fetch a collection.
152212
*

clients/js/test/helps/fetch.test.ts

+91
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
fetchAssetsByCollection,
66
fetchAssetsByOwner,
77
fetchCollectionsByUpdateAuthority,
8+
fetchAllAssets,
89
} from '../../src';
910
import { createUmi } from '../_setupRaw';
1011
import { createAsset, createCollection } from '../_setupSdk';
@@ -161,3 +162,93 @@ test('it can use helper to fetch assets by collection and derive plugins', async
161162
}
162163
});
163164
});
165+
166+
test('it can use helper to fetch all assets', async (t) => {
167+
const umi = await createUmi();
168+
169+
const collection = await createCollection(umi, {
170+
plugins: [
171+
{
172+
type: 'ImmutableMetadata',
173+
},
174+
],
175+
});
176+
177+
const assetsOfOwner1 = await Promise.all(
178+
Array(2)
179+
.fill(0)
180+
.map((_, index) =>
181+
createAsset(umi, {
182+
collection,
183+
name: `Asset ${index + 1}`,
184+
plugins: [
185+
{
186+
type: 'Attributes',
187+
attributeList: [
188+
{
189+
key: 'asset',
190+
value: 'asset',
191+
},
192+
],
193+
},
194+
],
195+
})
196+
)
197+
);
198+
199+
const assetsOfOwner2 = await Promise.all(
200+
Array(2)
201+
.fill(0)
202+
.map((_, index) =>
203+
createAsset(umi, {
204+
collection,
205+
name: `Asset ${index + 1}`,
206+
plugins: [
207+
{
208+
type: 'Attributes',
209+
attributeList: [
210+
{
211+
key: 'asset',
212+
value: 'asset',
213+
},
214+
],
215+
},
216+
],
217+
})
218+
)
219+
);
220+
221+
const allCreatedAssets = [...assetsOfOwner1, ...assetsOfOwner2];
222+
223+
const assetPublicKeys = [...assetsOfOwner1, ...assetsOfOwner2].map(
224+
(asset) => asset.publicKey
225+
);
226+
227+
const fetchedAssets = await fetchAllAssets(umi, assetPublicKeys);
228+
229+
const createdAssetPubkeys = allCreatedAssets
230+
.map((asset) => asset.publicKey)
231+
.sort();
232+
const fetchedAssetPubkeys = fetchedAssets
233+
.map((asset) => asset.publicKey)
234+
.filter((pubkey) => createdAssetPubkeys.includes(pubkey))
235+
.sort();
236+
237+
t.deepEqual(
238+
fetchedAssetPubkeys,
239+
createdAssetPubkeys,
240+
'All created assets should be found in fetched assets'
241+
);
242+
243+
t.deepEqual(
244+
fetchedAssets[0].attributes,
245+
allCreatedAssets[0].attributes,
246+
'Asset level attribute plugin should be found in fetched assets'
247+
);
248+
249+
t.deepEqual(
250+
fetchedAssets[0].immutableMetadata,
251+
collection.immutableMetadata,
252+
'Collection level immutableMetadata plugin should be found in fetched assets'
253+
);
254+
});

0 commit comments

Comments
 (0)