forked from mico/idle_moloch
114 lines
3.8 KiB
TypeScript
114 lines
3.8 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import styles from '../styles/Leaderboard.module.css'
|
|
import { TopEarnersResponse, TopRaidersResponse } from '../types/leaderboard'
|
|
import { formatUnits } from 'viem'
|
|
|
|
const SUBGRAPH_URL = 'https://api.studio.thegraph.com/query/75782/slay-the-moloch-base-sepolia/version/latest'
|
|
|
|
const Leaderboard = () => {
|
|
const [topEarners, setTopEarners] = useState<TopEarnersResponse>()
|
|
const [topRaiders, setTopRaiders] = useState<TopRaidersResponse>()
|
|
const [activeTab, setActiveTab] = useState<'earners' | 'raiders'>('earners')
|
|
|
|
useEffect(() => {
|
|
const fetchLeaderboards = async () => {
|
|
try {
|
|
// Fetch top earners
|
|
const earnersResponse = await fetch(SUBGRAPH_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
query: `{
|
|
armies(first: 10, orderBy: profitPerSecond, orderDirection: desc) {
|
|
player {
|
|
id
|
|
totalMinted
|
|
currentBalance
|
|
numberOfRaids
|
|
}
|
|
profitPerSecond
|
|
molochDenierLevel
|
|
apprenticeLevel
|
|
anointedLevel
|
|
championLevel
|
|
}
|
|
}`
|
|
})
|
|
})
|
|
const earnersData = await earnersResponse.json()
|
|
setTopEarners({ armies: earnersData.data.armies })
|
|
|
|
// Fetch top raiders
|
|
const raidersResponse = await fetch(SUBGRAPH_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
query: `{
|
|
players(first: 10, orderBy: numberOfRaids, orderDirection: desc) {
|
|
id
|
|
numberOfRaids
|
|
totalMinted
|
|
currentBalance
|
|
}
|
|
}`
|
|
})
|
|
})
|
|
const raidersData = await raidersResponse.json()
|
|
setTopRaiders({ players: raidersData.data.players })
|
|
} catch (error) {
|
|
console.error('Error fetching leaderboard:', error)
|
|
}
|
|
}
|
|
|
|
fetchLeaderboards()
|
|
const interval = setInterval(fetchLeaderboards, 30000) // Refresh every 30 seconds
|
|
return () => clearInterval(interval)
|
|
}, [])
|
|
|
|
return (
|
|
<div className={styles.leaderboard}>
|
|
<h2 className={styles.title}>Leaderboard</h2>
|
|
|
|
<div className={styles.tabs}>
|
|
<button
|
|
className={`${styles.tab} ${activeTab === 'earners' ? styles.active : ''}`}
|
|
onClick={() => setActiveTab('earners')}
|
|
>
|
|
Top Earners
|
|
</button>
|
|
<button
|
|
className={`${styles.tab} ${activeTab === 'raiders' ? styles.active : ''}`}
|
|
onClick={() => setActiveTab('raiders')}
|
|
>
|
|
Top Raiders
|
|
</button>
|
|
</div>
|
|
|
|
{activeTab === 'earners' && (
|
|
<div className={styles.list}>
|
|
{topEarners?.armies.map((army, index) => (
|
|
<div key={army.player.id} className={styles.item}>
|
|
<span className={styles.rank}>#{index + 1}</span>
|
|
<span className={styles.address}>{army.player.id.slice(0, 6)}...{army.player.id.slice(-4)}</span>
|
|
<span className={styles.stat}>{formatUnits(BigInt(army.profitPerSecond), 4)} GELD/s</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'raiders' && (
|
|
<div className={styles.list}>
|
|
{topRaiders?.players.map((player, index) => (
|
|
<div key={player.id} className={styles.item}>
|
|
<span className={styles.rank}>#{index + 1}</span>
|
|
<span className={styles.address}>{player.id.slice(0, 6)}...{player.id.slice(-4)}</span>
|
|
<span className={styles.stat}>{player.numberOfRaids} raids</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Leaderboard |