11import { AddressLookupTableAccount , Connection , PublicKey , PublicKeyInitData , TransactionInstruction } from "@solana/web3.js" ;
2- import { Metaplex } from "@metaplex-foundation/js" ;
2+ // import { Metaplex } from "@metaplex-foundation/js";
3+ import {
4+ fetchDigitalAssetWithAssociatedToken ,
5+ mplTokenMetadata ,
6+ findMetadataPda ,
7+ fetchDigitalAssetByMetadata ,
8+ fetchMetadata ,
9+ TokenStandard
10+ } from '@metaplex-foundation/mpl-token-metadata'
11+ import { fromWeb3JsPublicKey , toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters'
12+ import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
313import Bottleneck from "bottleneck" ;
414import { fetchIpfsMetadata } from "./fetchIpfsMetadata" ;
515import { extractCidFromUrl } from "./extractCidFromUrl" ;
@@ -10,6 +20,7 @@ import { Instruction } from "@jup-ag/api";
1020import { fetchFloorPrice } from "./fetchFloorPrice" ;
1121import { NETWORK } from "@utils/endpoints" ;
1222import { createJupiterApiClient , QuoteGetRequest } from "@jup-ag/api" ;
23+ import { getTokenInfo } from "./fetchDexTokenInfo" ;
1324
1425const ENDPOINT = NETWORK ;
1526
@@ -21,7 +32,7 @@ if (!ENDPOINT || (!ENDPOINT.startsWith('http:') && !ENDPOINT.startsWith('https:'
2132
2233// console.log(`ENDPOINT: ${ENDPOINT}`);
2334const connection = new Connection ( ENDPOINT ) ;
24- const metaplex = Metaplex . make ( connection ) ;
35+ const metaplexUmi = createUmi ( ENDPOINT ) . use ( mplTokenMetadata ( ) ) ;
2536const DEFAULT_IMAGE_URL = process . env . UNKNOWN_IMAGE_URL || "https://s3.coinmarketcap.com/static-gravity/image/5cc0b99a8dd84fbfa4e150d84b5531f2.png" ;
2637
2738// Modify the rate limiters at the top
@@ -113,34 +124,47 @@ export type TokenData = {
113124
114125export async function fetchTokenMetadata ( mintAddress : PublicKey , mint : string ) {
115126 try {
116- const metadataAccount = metaplex
117- . nfts ( )
118- . pdas ( )
119- . metadata ( { mint : mintAddress } ) ;
120-
121- const metadataAccountInfo = await withRetry ( ( ) =>
122- rpcLimiter . schedule ( ( ) => connection . getAccountInfo ( metadataAccount ) )
123- ) ;
127+ const metadataPda = findMetadataPda ( metaplexUmi , {
128+ mint : fromWeb3JsPublicKey ( mintAddress )
129+ } ) ;
130+ // console.log(`Metadata account: ${metadataPda}`);
131+ // const metadataAccountInfo = await withRetry(() =>
132+ // rpcLimiter.schedule(() => connection.getAccountInfo(new PublicKey(metadataPda)))
133+ // );
134+ const metadataAccountInfo = await fetchDigitalAssetByMetadata ( metaplexUmi , metadataPda ) ;
135+
136+ console . log ( `Metadata: ${ JSON . stringify ( metadataAccountInfo , ( _ , value ) =>
137+ typeof value === 'bigint' ? value . toString ( ) : value
138+ ) } `) ;
124139
125140 if ( ! metadataAccountInfo ) {
126141 return getDefaultTokenMetadata ( mint ) ;
127142 }
143+ const collectionMetadata = await fetchMetadata ( metaplexUmi , metadataPda ) ;
144+ const cid = collectionMetadata . uri ? extractCidFromUrl ( collectionMetadata . uri ) : null ;
145+ const logo = cid ? await fetchIpfsMetadata ( cid ) : null ;
128146
129147 const token = await withRetry ( ( ) =>
130- rpcLimiter . schedule ( ( ) => metaplex . nfts ( ) . findByMint ( { mintAddress : mintAddress } ) )
148+ rpcLimiter . schedule ( ( ) => fetchDigitalAssetByMetadata ( metaplexUmi , metadataPda ) )
131149 ) ;
132150
133- let metadata = await processTokenMetadata ( token , mint ) ;
134-
151+ let metadata = await processTokenMetadata ( token , logo ?. imageUrl ?? '' , cid ?? '' , mint ) ;
135152 // Handle collection metadata separately to prevent failures
136- if ( token . collection ) {
153+ const tokenStandard = metadataAccountInfo ?. metadata ?. tokenStandard ?. valueOf ( ) ;
154+ const isNft = tokenStandard === TokenStandard . NonFungible ||
155+ tokenStandard === TokenStandard . NonFungibleEdition ||
156+ tokenStandard === TokenStandard . ProgrammableNonFungible ||
157+ tokenStandard === TokenStandard . ProgrammableNonFungibleEdition ;
158+ console . log ( `isNft: ${ isNft } ` ) ;
159+ if ( isNft ) {
160+ const collectionName = collectionMetadata ?. name ?? metadata . name ;
161+ const collectionLogo = logo ?. imageUrl ?? DEFAULT_IMAGE_URL ;
137162 try {
138- const collectionMetadata = await fetchCollectionMetadata ( token . collection . address ) ;
139163 metadata = {
140164 ...metadata ,
141- collectionName : collectionMetadata ?. name ?? metadata . name ,
142- collectionLogo : collectionMetadata ?. logo ?? metadata . logo ,
143- isNft : true
165+ collectionName,
166+ collectionLogo,
167+ isNft
144168 } ;
145169 } catch ( collectionError ) {
146170 console . warn ( `Failed to fetch collection metadata for token ${ mint } :` , collectionError ) ;
@@ -156,119 +180,44 @@ export async function fetchTokenMetadata(mintAddress: PublicKey, mint: string) {
156180 }
157181}
158182
159- async function fetchCollectionMetadata ( collectionAddress : PublicKey ) {
160- try {
161- const metadataAccount = metaplex
162- . nfts ( )
163- . pdas ( )
164- . metadata ( { mint : collectionAddress } ) ;
165-
166- // Wrap RPC calls with withRetry and rpcLimiter
167- const metadataAccountInfo = await withRetry ( ( ) =>
168- rpcLimiter . schedule ( ( ) =>
169- connection . getAccountInfo ( metadataAccount )
170- )
171- ) ;
172-
173- if ( ! metadataAccountInfo ) {
174- console . log ( `No metadata account found for collection: ${ collectionAddress . toString ( ) } ` ) ;
175- return getDefaultMetadata ( ) ;
176- }
177-
178- const collection = await withRetry ( ( ) =>
179- rpcLimiter . schedule ( ( ) =>
180- metaplex . nfts ( ) . findByMint ( { mintAddress : collectionAddress } )
181- )
182- ) ;
183-
184- const cid = extractCidFromUrl ( collection . uri ) ;
185- if ( cid ) {
186- try {
187- const collectionMetadata = await apiLimiter . schedule ( ( ) =>
188- fetchIpfsMetadata ( cid )
189- ) ;
190- return {
191- name : collection . name || "Unknown Collection" ,
192- symbol : collection . symbol || "UNKNOWN" ,
193- logo : collectionMetadata . imageUrl ?? collection . json ?. image ?? DEFAULT_IMAGE_URL ,
194- cid : cid ,
195- isNft : true
196- } ;
197- } catch ( ipfsError ) {
198- console . warn ( `Failed to fetch IPFS metadata for collection ${ collectionAddress . toString ( ) } :` , ipfsError ) ;
199- return {
200- name : collection . name || "Unknown Collection" ,
201- symbol : collection . symbol || "UNKNOWN" ,
202- logo : collection . json ?. image ?? DEFAULT_IMAGE_URL ,
203- cid : cid ,
204- isNft : true
205- } ;
206- }
207- }
208-
209- return {
210- name : collection . name || "Unknown Collection" ,
211- symbol : collection . symbol || "UNKNOWN" ,
212- logo : collection . json ?. image ?? DEFAULT_IMAGE_URL ,
213- cid : null ,
214- isNft : true
215- } ;
216-
217- } catch ( error ) {
218- console . warn ( "Error fetching collection metadata for address:" , collectionAddress . toString ( ) , error ) ;
219- return getDefaultMetadata ( ) ;
220- }
221- }
222-
223- // Add a helper function to return default metadata
224- function getDefaultMetadata ( ) {
225- return {
226- name : "Unknown Collection" ,
227- symbol : "UNKNOWN" ,
228- logo : DEFAULT_IMAGE_URL ,
229- cid : null ,
230- isNft : true
231- } ;
232- }
233-
234183// Helper function to get default token metadata
235- function getDefaultTokenMetadata ( mint : string ) {
184+ async function getDefaultTokenMetadata ( mint : string ) {
185+ const tokenInfo = await getTokenInfo ( mint ) ;
236186 return {
237- name : mint ,
238- symbol : mint ,
239- logo : DEFAULT_IMAGE_URL ,
187+ name : tokenInfo ?. name || mint ,
188+ symbol : tokenInfo ?. symbol || mint ,
189+ logo : tokenInfo ?. image || DEFAULT_IMAGE_URL ,
240190 cid : null ,
241191 collectionName : mint ,
242- collectionLogo : DEFAULT_IMAGE_URL ,
192+ collectionLogo : tokenInfo ?. image || DEFAULT_IMAGE_URL ,
243193 isNft : false
244194 } ;
245195}
246196
247197// Helper function to process token metadata
248- async function processTokenMetadata ( token : any , mint : string ) {
249- const cid = extractCidFromUrl ( token . uri ) ;
198+ async function processTokenMetadata ( token : any , logo : string , cid : string , mint : string ) {
199+ console . log ( `Token metadata: ${ JSON . stringify ( token , ( _ , value ) =>
200+ typeof value === 'bigint' ? value . toString ( ) : value
201+ ) } `) ;
202+ let tokenName = mint ;
203+ let symbol = mint ;
204+ if ( logo . length > 0 ) {
205+ logo = logo ;
206+ } else {
207+ const tokenInfo = await getTokenInfo ( mint ) ;
208+ tokenName = tokenInfo ?. name ?? mint ;
209+ symbol = tokenInfo ?. symbol ?? mint ;
210+ logo = tokenInfo ?. image ?? DEFAULT_IMAGE_URL ;
211+ }
250212 let metadata = {
251- name : token ?. name || mint ,
252- symbol : token ?. symbol || mint ,
253- logo : token . json ?. image ?? DEFAULT_IMAGE_URL ,
254- cid,
255- collectionName : token ?. name || mint ,
256- collectionLogo : token . json ?. image ?? DEFAULT_IMAGE_URL ,
213+ name : token ?. metadata ?. name || tokenName ,
214+ symbol : token ?. metadata ?. symbol || symbol ,
215+ logo : logo ,
216+ cid : cid ,
217+ collectionName : token ?. metadata ?. name || tokenName ,
218+ collectionLogo : logo ?? DEFAULT_IMAGE_URL ,
257219 isNft : false
258220 } ;
259-
260- if ( cid ) {
261- try {
262- const newMetadata = await apiLimiter . schedule ( ( ) =>
263- fetchIpfsMetadata ( cid )
264- ) ;
265- metadata . logo = newMetadata . imageUrl ?? token . json ?. image ?? DEFAULT_IMAGE_URL ;
266- } catch ( ipfsError ) {
267- console . warn ( `Failed to fetch IPFS metadata for token ${ mint } :` , ipfsError ) ;
268- // Keep existing metadata if IPFS fetch fails
269- }
270- }
271-
272221 return metadata ;
273222}
274223
0 commit comments