export * from './routeScan'; import { vmTypes, EVMNFTResponse } from "../types"; import { RouteScanAPI } from './routeScan'; const BASE_URL = "https://helper.decane.app" export class HelperAPI { /** * Get network name from chainId for RouteScan API * Defaults to 'mainnet' for most chains */ private static getNetworkFromChainId(chainId: number): string { // Map common testnet chain IDs to 'testnet' const testnets = [11155111, 80001, 5, 97, 421614, 84532]; // Sepolia, Mumbai, Goerli, BSC Testnet, Arbitrum Sepolia, Base Sepolia if (testnets.includes(chainId)) { return 'testnet'; } return 'mainnet'; } static async getPrice(tokenOrNativeSymbol: string, chainId: number, vm: vmTypes) { const res = await fetch("" + BASE_URL + `/${vm}/prices?chainId${chainId}tokenAddresses=${tokenOrNativeSymbol}`); const data = await res.json(); return data; } static async getUserToken(wallet: string, vm: vmTypes, chainId: number) { // Only try RouteScan for EVM chains if (vm === 'EVM') { try { const network = this.getNetworkFromChainId(chainId); const routeScanData = await RouteScanAPI.getTokenTransfers( network, chainId, wallet, undefined, // contractaddress 0, // startblock 99999999, // endblock 1, // page 100, // offset - get more results 'desc' // sort by newest first ); // If RouteScan succeeds and has valid data, transform to expected format if (routeScanData && routeScanData.status === '1' && routeScanData.result) { // Group token transfers by contract address to get unique tokens const tokenMap = new Map(); for (const transfer of routeScanData.result) { const address = transfer.contractAddress.toLowerCase(); if (!tokenMap.has(address)) { tokenMap.set(address, { contractAddress: transfer.contractAddress, name: transfer.tokenName, symbol: transfer.tokenSymbol, decimals: parseInt(transfer.tokenDecimal) || 0, balance: '0', // RouteScan doesn't provide current balance logo: undefined }); } } // Transform to expected format with 'data' field return { data: Array.from(tokenMap.values()) }; } } catch (error) { console.warn('RouteScan API failed, falling back to helper.decane.app:', error); } } // Fallback to original API const res = await fetch("" + BASE_URL + `/${vm}/tokens/discover?wallet=${wallet}&chainId=${chainId}`); const data = await res.json(); return data; } static async getUserNFTs(wallet: string, vm: vmTypes, chainId: number): Promise { // Only try RouteScan for EVM chains if (vm === 'EVM') { try { const network = this.getNetworkFromChainId(chainId); const routeScanData = await RouteScanAPI.getNftTransfers( network, chainId, wallet, undefined, // contractaddress 0, // startblock 99999999, // endblock 1, // page 100, // offset 'desc' // sort by newest first ); // If RouteScan succeeds and has valid data, transform to EVMNFTResponse format if (routeScanData && routeScanData.status === '1' && routeScanData.result) { // Map RouteScan NFT transfers to EVMNFTResponse format const nfts = routeScanData.result.map(nftTx => ({ contract: { address: nftTx.contractAddress, name: nftTx.tokenName, symbol: nftTx.tokenSymbol, totalSupply: '0', tokenType: 'ERC721', // Default, RouteScan doesn't specify contractDeployer: '', deployedBlockNumber: parseInt(nftTx.blockNumber) || 0, isSpam: false }, tokenId: nftTx.tokenID, tokenType: 'ERC721', // Default name: nftTx.tokenName, description: '', balance: '1', // Default for NFTs timeLastUpdated: nftTx.timeStamp, acquiredAt: nftTx.timeStamp })); return { data: nfts }; } } catch (error) { console.warn('RouteScan API failed, falling back to helper.decane.app:', error); } } // Fallback to original API const res = await fetch("" + BASE_URL + `/${vm}/nfts/discover?wallet=${wallet}&chainId=${chainId}`); const data = await res.json(); return data; } static async getTransactionHistory(wallet: string, vm: vmTypes, chainId: number, limit: number = 20) { // Only try RouteScan for EVM chains if (vm === 'EVM') { try { const network = this.getNetworkFromChainId(chainId); const routeScanData = await RouteScanAPI.getTxList( network, chainId, wallet, 0, // startblock 99999999, // endblock 1, // page limit, // offset (number of results) 'desc' // sort by newest first ); // If RouteScan succeeds and has valid data, transform it to expected format // Return RouteScan response as-is - consumer will handle the transformation if (routeScanData && routeScanData.status === '1' && routeScanData.result) { return routeScanData; } } catch (error) { console.warn('RouteScan API failed, falling back to helper.decane.app:', error); } } // Fallback to original API const res = await fetch("" + BASE_URL + `/${vm}/transactions/history?wallet=${wallet}&chainId=${chainId}&limit=${limit}`); const data = await res.json(); return data; } }