Skip to content

Commit 37156e9

Browse files
authored
Merge pull request #791 from ahsan-javaiid/feat/nft/rootstock
Enable NFT tab on Rootstock
2 parents 9ff1468 + 7f1cf13 commit 37156e9

3 files changed

Lines changed: 117 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { NFTCollection, NFTItem, NFTType } from '@/types/nft';
2+
import { BlockscoutNFTItem } from '@/libs/nft-handlers/types/blockscout';
3+
import { NodeType } from '@/types/provider';
4+
import Networks from '@/providers/ethereum/networks';
5+
import cacheFetch from '../cache-fetch';
6+
import { ethers, Contract } from 'ethers';
7+
const ROOTSTOCK_RPC_NODE = 'https://public-node.rsk.co';
8+
const ERC721_METADATA_ABI = [
9+
'function contractURI() view returns (string)'
10+
];
11+
const BLOCKSCOUT_ENDPOINT = 'https://rootstock.blockscout.com/api/v2/addresses';
12+
const IPFS_ENDPOINT = 'https://ipfs.io/ipfs';
13+
const CACHE_TTL = 60 * 1000;
14+
const getAssetURL = (uri: string) => {
15+
if (uri && uri.startsWith('ipfs://')) {
16+
return `${IPFS_ENDPOINT}/${uri.slice(7)}`;
17+
}
18+
19+
return uri;
20+
};
21+
const fetchNftCollectionDescription = async (network: NodeType, contractAddress: string): Promise<string | null> => {
22+
try {
23+
const supportedNetworks = [Networks.rootstock.name];
24+
if (!supportedNetworks.includes(network.name)) {
25+
return null;
26+
}
27+
const provider = new ethers.providers.JsonRpcProvider(ROOTSTOCK_RPC_NODE);
28+
const contract = new Contract(contractAddress.toLowerCase(), ERC721_METADATA_ABI, provider);
29+
const uri = await contract.contractURI();
30+
const httpUrl = getAssetURL(uri);
31+
const response = await fetch(httpUrl);
32+
if (!response.ok) {
33+
return null;
34+
}
35+
const metadata = await response.json();
36+
return metadata.description;
37+
} catch {
38+
return null;
39+
}
40+
}
41+
export default async (
42+
network: NodeType,
43+
address: string,
44+
): Promise<NFTCollection[]> => {
45+
const supportedNetworks = [Networks.rootstock.name];
46+
if (!supportedNetworks.includes(network.name))
47+
throw new Error('Blockscout: network not supported');
48+
const fetchAll = (): Promise<BlockscoutNFTItem[]> => {
49+
const query = `${BLOCKSCOUT_ENDPOINT}/${address}/nft/collections?type=ERC-721`;
50+
return cacheFetch({ url: query }, CACHE_TTL).then(json => {
51+
return json.items as BlockscoutNFTItem[];
52+
});
53+
};
54+
const allItems = await fetchAll();
55+
if (!allItems || !allItems.length) return [];
56+
return Promise.all(allItems.map(async item => {
57+
const ret: NFTCollection = {
58+
name: item.token.name ?? 'Unnamed token',
59+
description: await fetchNftCollectionDescription(network, item.token.address_hash),
60+
image: item.token?.icon_url,
61+
contract: item.token.address_hash,
62+
items: item.token_instances.map(asset => {
63+
const retAsset: NFTItem = {
64+
contract: item.token.address_hash,
65+
id: asset.id,
66+
image: getAssetURL(asset.media_url),
67+
name: asset?.metadata?.name ?? 'Unnamed NFT asset',
68+
url: `https://rootstock.blockscout.com/token/${item.token.address_hash}/instance/${asset.id}`,
69+
type: NFTType.ERC721,
70+
};
71+
return retAsset;
72+
}),
73+
};
74+
return ret;
75+
}));
76+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
export type BlockscoutNFTItem = {
2+
amount: string;
3+
token: Token;
4+
token_instances: TokenInstance[];
5+
};
6+
7+
export type Token = {
8+
address_hash: string;
9+
holders_count: string;
10+
icon_url: string;
11+
name: string | null;
12+
type: string;
13+
};
14+
15+
export type TokenInstance = {
16+
id: string;
17+
image_url: string;
18+
media_url: string;
19+
metadata: TokenMetadata;
20+
owner: string;
21+
token: Token;
22+
token_type: string;
23+
};
24+
25+
export type TokenMetadata = {
26+
attributes?: Attribute[];
27+
background_color?: string;
28+
description?: string;
29+
external_url?: string;
30+
image_data?: string;
31+
name: string;
32+
image?: string;
33+
};
34+
35+
export type Attribute = {
36+
trait_type: string;
37+
value: string | number;
38+
display_type?: string;
39+
};

packages/extension/src/providers/ethereum/networks/rsk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
isValidAddress,
1010
} from '@ethereumjs/util';
1111
import assetsInfoHandler from '@/providers/ethereum/libs/assets-handlers/assetinfo-mew';
12+
import NFTHandler from '@/libs/nft-handlers/blockscout';
1213

1314
const rootstockOptions: EvmNetworkOptions = {
1415
name: NetworkNames.Rootstock,
@@ -26,6 +27,7 @@ const rootstockOptions: EvmNetworkOptions = {
2627
coingeckoID: CoingeckoPlatform.Rootstock,
2728
coingeckoPlatform: CoingeckoPlatform.Rootstock,
2829
activityHandler: wrapActivityHandler(EtherscanActivity),
30+
NFTHandler,
2931
assetsInfoHandler,
3032
};
3133

0 commit comments

Comments
 (0)