dexscreener-chrome-extension/content.js

109 lines
4.2 KiB
JavaScript

const DEXSCREENER_API_URL = "https://api.dexscreener.io/latest/dex/tokens/";
const fetchedTickers = new Set();
function isSmartContract(address) {
// Patterns for different blockchain addresses
const addressMatch = address.match(
/\b(0x[a-fA-F0-9]{40}|[1-9A-HJ-NP-Za-km-z]{33,34}|[A-HJ-NP-Za-km-z1-9]{58}|tz1[1-9A-HJ-NP-Za-km-z]{33}|erd1[a-z0-9]{58}|ckt1[a-z0-9]{42}|io[a-z0-9]{38}|terra1[a-z0-9]{38}|cosmos1[a-z0-9]{38}|osmo1[a-z0-9]{38}|cro1[a-z0-9]{38}|bc1[a-z0-9]{39}|nano_[13][a-z0-9]{59}|aave_[a-zA-Z0-9]{64}|erd1[a-z0-9]{38}|bsc1[a-z0-9]{38}|G[a-zA-Z0-9]{43})\b/
);
if(!addressMatch){
return "na";
}
return addressMatch[0];
}
function fetchTokenData(contractAddress, x, y) {
const url = `${DEXSCREENER_API_URL}${contractAddress}`;
console.log(url);
if (fetchedTickers.has(contractAddress)) {
return; // Ticker already fetched, do nothing
}
fetchedTickers.add(contractAddress); // Add the ticker to the set
try {
chrome.runtime.sendMessage({ action: "fetchData", url: url }, (response) => {
if (chrome.runtime.lastError) {
console.error("Error: ", chrome.runtime.lastError.message);
fetchedTickers.delete(contractAddress); // Cleanup in case of error
return;
}
const data = response.data.pairs[0]; // Assuming the first pair is what you want
if (data) {
const tooltip = createTooltip(contractAddress, data.baseToken.symbol, data.priceUsd || data.priceNative, data.url, x, y);
document.body.appendChild(tooltip);
monitorCursor(tooltip, contractAddress);
}
});
} catch (error) {
console.error("Failed to send message to background script: ", error);
fetchedTickers.delete(contractAddress); // Cleanup in case of error
}
}
function extractTicker(text) {
const index = text.indexOf('0x');
if (index !== -1 && text.length >= index + 42) { // 2 characters for '0x' + 40 characters for the address
return text.substring(index, index + 42); // Extract '0x' + 40 characters
}
return null;
}
function createTooltip(contractAddress, ticker, price, chartUrl, x, y) {
const tooltip = document.createElement('div');
tooltip.className = 'dex-tooltip';
tooltip.dataset.contractAddress = contractAddress;
tooltip.style.position = 'absolute';
tooltip.style.background = 'rgba(255, 255, 255, 0.05)'; // Semi-transparent white background
tooltip.style.backdropFilter = 'blur(10px)'; // Blur effect behind the tooltip
tooltip.style.color = '#fff'; // White text color
tooltip.style.padding = '10px'; // Increased padding for better look
tooltip.style.borderRadius = '10px'; // Rounded corners
tooltip.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; // Subtle shadow for depth
tooltip.style.zIndex = '9999';
tooltip.style.left = `${x}px`;
tooltip.style.top = `${y}px`;
tooltip.innerHTML = `Ticker: ${ticker}<br>Price: ${price}`;
tooltip.addEventListener('click', () => window.open(chartUrl, '_blank'));
return tooltip;
}
function monitorCursor(tooltip, contractAddress) {
const distanceThreshold = 100; // Max distance in pixels before the tooltip disappears
function onMouseMove(event) {
const rect = tooltip.getBoundingClientRect();
const dx = Math.max(rect.left - event.clientX, event.clientX - rect.right);
const dy = Math.max(rect.top - event.clientY, event.clientY - rect.bottom);
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > distanceThreshold) {
document.body.removeChild(tooltip);
fetchedTickers.delete(contractAddress);
document.removeEventListener('mousemove', onMouseMove);
}
}
document.addEventListener('mousemove', onMouseMove);
}
function handleMouseOver(event) {
const target = event.target;
if (target.nodeType === Node.TEXT_NODE || target.nodeType === Node.ELEMENT_NODE) {
const text = target.textContent.trim();
const extractedTicker = isSmartContract(text);
if(extractedTicker == "na"){
return;
}
const rect = target.getBoundingClientRect(); // Get the target's position relative to the viewport
const x = rect.left + window.scrollX; // X-coordinate
const y = rect.top + window.scrollY;
fetchTokenData(extractedTicker, x, y);
}
}
document.addEventListener('mouseover', handleMouseOver);