File size: 8,224 Bytes
611da1f 1646b03 77a0b16 1646b03 77a0b16 611da1f 77a0b16 611da1f 77a0b16 1646b03 03fe710 77a0b16 611da1f 1646b03 611da1f 1646b03 611da1f 77a0b16 611da1f 77a0b16 611da1f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
// Configuration
const GAMMA_API = 'https://gamma-api.polymarket.com/markets';
const CLOB_API = 'https://clob.polymarket.com/markets';
const DATA_API = 'https://data-api.polymarket.com/trades?limit=5';
const FALLBACK_API = 'https://strapi-matic.poly.market/markets'; // Fallback if gamma fails
const DEFAULT_PARAMS = {
limit: 20,
order: 'volumeNum desc',
active: true
};
const USE_FALLBACK = true;
const REFRESH_INTERVAL = 30000; // 30 seconds
// DOM Elements
const marketsContainer = document.getElementById('markets-container');
const loadingElement = document.getElementById('loading');
const errorMessage = document.getElementById('error-message');
const refreshBtn = document.getElementById('refresh-btn');
const retryBtn = document.getElementById('retry-btn');
const categoryFilter = document.getElementById('category-filter');
// State
let marketsData = [];
let isLoading = false;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
fetchMarkets();
// Set up refresh interval
setInterval(fetchMarkets, REFRESH_INTERVAL);
// Event listeners
refreshBtn.addEventListener('click', fetchMarkets);
retryBtn.addEventListener('click', fetchMarkets);
categoryFilter.addEventListener('change', filterMarkets);
});
// Fetch markets data
async function fetchMarkets() {
if (isLoading) return;
isLoading = true;
loadingElement.classList.remove('hidden');
errorMessage.classList.add('hidden');
marketsContainer.innerHTML = '';
try {
// Build URL with query params
const url = new URL(GAMMA_API);
Object.entries(DEFAULT_PARAMS).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
// Try primary Gamma API first
let response;
try {
response = await fetch(url, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
if (!response.ok) throw new Error('Gamma API failed');
const data = await response.json();
marketsData = data.map(market => ({
id: market.id,
title: market.question,
category: market.category,
volume: parseFloat(market.volumeNum),
liquidity: parseFloat(market.liquidityNum),
endDate: market.endDate,
imageUrl: market.imageUrl || 'http://static.photos/abstract/640x360',
outcomes: market.outcomePrices.split(',').map((price, i) => ({
name: i === 0 ? 'Yes' : 'No',
price: parseFloat(price),
probability: parseFloat(price)
}))
}));
renderMarkets(marketsData);
} catch (error) {
console.error('Gamma API failed, trying fallback:', error);
// Try fallback API if gamma fails
try {
const fallbackUrl = new URL(FALLBACK_API);
Object.entries(DEFAULT_PARAMS).forEach(([key, value]) => {
fallbackUrl.searchParams.append(key, value);
});
response = await fetch(fallbackUrl);
if (!response.ok) throw new Error('Fallback API failed');
const data = await response.json();
// Process fallback data differently if needed
marketsData = data.map(market => ({
id: market.id,
title: market.question,
category: market.category,
volume: parseFloat(market.volumeNum),
liquidity: parseFloat(market.liquidityNum),
endDate: market.endDate,
imageUrl: market.imageUrl || 'http://static.photos/abstract/640x360',
outcomes: market.outcomePrices.split(',').map((price, i) => ({
name: i === 0 ? 'Yes' : 'No',
price: parseFloat(price),
probability: parseFloat(price)
}))
}));
renderMarkets(marketsData);
showWarning('Using fallback data. Gamma API unavailable.');
} catch (fallbackError) {
console.error('Fallback API also failed:', fallbackError);
showError();
// If we have cached data, show it with a warning
if (marketsData.length > 0) {
renderMarkets(marketsData);
showWarning('Showing cached data. Connection issues detected.');
}
}
} finally {
isLoading = false;
loadingElement.classList.add('hidden');
}
}
// Render markets
function renderMarkets(markets) {
marketsContainer.innerHTML = '';
if (markets.length === 0) {
marketsContainer.innerHTML = `
<div class="col-span-full text-center py-10">
<i data-feather="inbox" class="w-12 h-12 text-gray-400 mx-auto"></i>
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200 mt-4">No markets found</h2>
<p class="text-gray-600 dark:text-gray-400 mt-2">Try adjusting your filters</p>
</div>
`;
feather.replace();
return;
}
markets.forEach(market => {
const marketCard = document.createElement('market-card');
marketCard.setAttribute('title', market.title);
marketCard.setAttribute('category', market.category);
marketCard.setAttribute('volume', market.volume.toLocaleString());
marketCard.setAttribute('liquidity', market.liquidity.toLocaleString());
marketCard.setAttribute('end-date', new Date(market.endDate).toLocaleDateString());
marketCard.setAttribute('image-url', market.imageUrl || 'http://static.photos/abstract/640x360');
// Add outcomes
market.outcomes.forEach((outcome, index) => {
marketCard.setAttribute(`outcome-${index}-name`, outcome.name);
marketCard.setAttribute(`outcome-${index}-price`, outcome.price.toFixed(2));
marketCard.setAttribute(`outcome-${index}-probability`, (outcome.probability * 100).toFixed(1));
});
marketsContainer.appendChild(marketCard);
});
feather.replace();
}
// Filter markets by category
function filterMarkets() {
const selectedCategory = categoryFilter.value;
if (selectedCategory === 'all') {
renderMarkets(marketsData);
return;
}
const filteredMarkets = marketsData.filter(market =>
market.category === selectedCategory
);
renderMarkets(filteredMarkets);
}
// Enhanced market data processing
function processMarketData(rawData) {
return rawData.map(market => {
const outcomes = market.outcomePrices.split(',');
return {
...market,
outcomes: [
{ name: 'Yes', price: parseFloat(outcomes[0]), probability: parseFloat(outcomes[0]) },
{ name: 'No', price: parseFloat(outcomes[1]), probability: parseFloat(outcomes[1]) }
],
volume: market.volumeNum,
liquidity: market.liquidityNum
};
});
}
// Fetch recent trades for a market (optional)
async function fetchMarketTrades(marketId) {
try {
const response = await fetch(`${DATA_API}&market=${marketId}`);
if (!response.ok) return null;
return await response.json();
} catch (error) {
console.error('Error fetching trades:', error);
return null;
}
}
// Show error message
function showError() {
errorMessage.classList.remove('hidden');
marketsContainer.innerHTML = '';
}
// Show warning message
function showWarning(message) {
const warningElement = document.createElement('div');
warningElement.className = 'bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-6';
warningElement.innerHTML = `
<div class="flex items-center">
<i data-feather="alert-triangle" class="w-5 h-5 mr-2"></i>
<p>${message}</p>
</div>
`;
marketsContainer.parentNode.insertBefore(warningElement, marketsContainer);
feather.replace();
}
|