Compare commits

..

2 Commits

Author SHA1 Message Date
eb4ce2d2c7 Merge pull request 'Leaderboard update' (#13) from leaderboardUpdate into main
Some checks are pending
CI / Foundry project (push) Waiting to run
Reviewed-on: #13
2024-11-01 15:10:57 +00:00
syahirAmali
3332eaf81e Leaderboard update
Some checks failed
CI / Foundry project (push) Has been cancelled
CI / Foundry project (pull_request) Has been cancelled
2024-11-01 23:08:54 +08:00
2 changed files with 217 additions and 108 deletions

View File

@ -1,22 +1,31 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from "react";
import styles from '../styles/Leaderboard.module.css' import styles from "../styles/Leaderboard.module.css";
import { TopEarnersResponse, TopRaidersResponse } from '../types/leaderboard' import {
import { formatUnits } from 'viem' TopEarnersResponse,
TopRaidersResponse,
PlayerResponse,
} 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 SUBGRAPH_URL =
"https://api.studio.thegraph.com/query/75782/slay-the-moloch-base-mainnet/version/latest";
const Leaderboard = () => { const Leaderboard = () => {
const [topEarners, setTopEarners] = useState<TopEarnersResponse>() const [topEarners, setTopEarners] = useState<TopEarnersResponse>();
const [topRaiders, setTopRaiders] = useState<TopRaidersResponse>() const [topRaiders, setTopRaiders] = useState<TopRaidersResponse>();
const [activeTab, setActiveTab] = useState<'earners' | 'raiders'>('earners') const [activeTab, setActiveTab] = useState<
"earners" | "raiders" | "bosses" | "prestige"
>("earners");
const [bossesDefeated, setBossesDefeated] = useState<PlayerResponse>();
const [playerPrestige, setPlayerPrestige] = useState<PlayerResponse>();
useEffect(() => { useEffect(() => {
const fetchLeaderboards = async () => { const fetchLeaderboards = async () => {
try { try {
// Fetch top earners // Fetch top earners
const earnersResponse = await fetch(SUBGRAPH_URL, { const earnersResponse = await fetch(SUBGRAPH_URL, {
method: 'POST', method: "POST",
headers: { 'Content-Type': 'application/json' }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
query: `{ query: `{
armies(first: 10, orderBy: profitPerSecond, orderDirection: desc) { armies(first: 10, orderBy: profitPerSecond, orderDirection: desc) {
@ -32,16 +41,16 @@ const Leaderboard = () => {
anointedLevel anointedLevel
championLevel championLevel
} }
}` }`,
}) }),
}) });
const earnersData = await earnersResponse.json() const earnersData = await earnersResponse.json();
setTopEarners({ armies: earnersData.data.armies }) setTopEarners({ armies: earnersData.data.armies });
// Fetch top raiders // Fetch top raiders
const raidersResponse = await fetch(SUBGRAPH_URL, { const raidersResponse = await fetch(SUBGRAPH_URL, {
method: 'POST', method: "POST",
headers: { 'Content-Type': 'application/json' }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
query: `{ query: `{
players(first: 10, orderBy: numberOfRaids, orderDirection: desc) { players(first: 10, orderBy: numberOfRaids, orderDirection: desc) {
@ -50,20 +59,52 @@ const Leaderboard = () => {
totalMinted totalMinted
currentBalance currentBalance
} }
}` }`,
}) }),
}) });
const raidersData = await raidersResponse.json() const raidersData = await raidersResponse.json();
setTopRaiders({ players: raidersData.data.players }) setTopRaiders({ players: raidersData.data.players });
} catch (error) {
console.error('Error fetching leaderboard:', error)
}
}
fetchLeaderboards() // Fetch bosses defeated
const interval = setInterval(fetchLeaderboards, 30000) // Refresh every 30 seconds const bossesDefeatedResponse = await fetch(SUBGRAPH_URL, {
return () => clearInterval(interval) method: "POST",
}, []) headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `{
players(first: 10, orderBy: bossesDefeated, orderDirection: desc) {
id
bossesDefeated
}
}`,
}),
});
const bossesDefeatedData = await bossesDefeatedResponse.json();
setBossesDefeated({ players: bossesDefeatedData.data.players });
// Fetch player prestige
const playerPrestigeResponse = await fetch(SUBGRAPH_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `{
players(first: 10, orderBy: prestigeLevel, orderDirection: desc) {
id
prestigeLevel
}
}`,
}),
});
const playerPrestigeData = await playerPrestigeResponse.json();
setPlayerPrestige({ players: playerPrestigeData.data.players });
} catch (error) {
console.error("Error fetching leaderboard:", error);
}
};
fetchLeaderboards();
const interval = setInterval(fetchLeaderboards, 30000); // Refresh every 30 seconds
return () => clearInterval(interval);
}, []);
return ( return (
<div className={styles.leaderboard}> <div className={styles.leaderboard}>
@ -71,44 +112,106 @@ const Leaderboard = () => {
<div className={styles.tabs}> <div className={styles.tabs}>
<button <button
className={`${styles.tab} ${activeTab === 'earners' ? styles.active : ''}`} className={`${styles.tab} ${
onClick={() => setActiveTab('earners')} activeTab === "earners" ? styles.active : ""
}`}
onClick={() => setActiveTab("earners")}
> >
Top Earners Top Earners
</button> </button>
<button <button
className={`${styles.tab} ${activeTab === 'raiders' ? styles.active : ''}`} className={`${styles.tab} ${
onClick={() => setActiveTab('raiders')} activeTab === "raiders" ? styles.active : ""
}`}
onClick={() => setActiveTab("raiders")}
> >
Top Raiders Top Raiders
</button> </button>
<button
className={`${styles.tab} ${
activeTab === "bosses" ? styles.active : ""
}`}
onClick={() => setActiveTab("bosses")}
>
Top Boss Slayers
</button>
<button
className={`${styles.tab} ${
activeTab === "prestige" ? styles.active : ""
}`}
onClick={() => setActiveTab("prestige")}
>
Top Players by Prestige
</button>
</div> </div>
{activeTab === 'earners' && ( {activeTab === "earners" && (
<div className={styles.list}> <div className={styles.list}>
{topEarners?.armies.map((army, index) => ( {topEarners?.armies.map((army, index) => (
<div key={army.player.id} className={styles.item}> <div key={army.player.id} className={styles.item}>
<span className={styles.rank}>#{index + 1}</span> <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.address}>
<span className={styles.stat}>{formatUnits(BigInt(army.profitPerSecond), 4)} GELD/s</span> {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>
))} ))}
</div> </div>
)} )}
{activeTab === 'raiders' && ( {activeTab === "raiders" && (
<div className={styles.list}> <div className={styles.list}>
{topRaiders?.players.map((player, index) => ( {topRaiders?.players.map((player, index) => (
<div key={player.id} className={styles.item}> <div key={player.id} className={styles.item}>
<span className={styles.rank}>#{index + 1}</span> <span className={styles.rank}>#{index + 1}</span>
<span className={styles.address}>{player.id.slice(0, 6)}...{player.id.slice(-4)}</span> <span className={styles.address}>
<span className={styles.stat}>{player.numberOfRaids} raids</span> {player.id.slice(0, 6)}...{player.id.slice(-4)}
</span>
<span className={styles.stat}>
{player.numberOfRaids} raids
</span>
</div>
))}
</div>
)}
{activeTab === "bosses" && (
<div className={styles.list}>
{bossesDefeated?.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.bossesDefeated} Bosses Slain
</span>
</div>
))}
</div>
)}
{activeTab === "prestige" && (
<div className={styles.list}>
{playerPrestige?.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.prestigeLevel} Prestige Level
</span>
</div> </div>
))} ))}
</div> </div>
)} )}
</div> </div>
) );
} };
export default Leaderboard export default Leaderboard;

View File

@ -1,25 +1,31 @@
export interface Player { export interface Player {
id: string id: string;
totalMinted: string totalMinted: string;
currentBalance: string currentBalance: string;
numberOfRaids: string numberOfRaids: string;
army?: Army army?: Army;
bossesDefeated?: string;
prestigeLevel: string;
} }
export interface Army { export interface Army {
player: Player player: Player;
profitPerSecond: string profitPerSecond: string;
projectedDailyEarnings: string projectedDailyEarnings: string;
molochDenierLevel: string molochDenierLevel: string;
apprenticeLevel: string apprenticeLevel: string;
anointedLevel: string anointedLevel: string;
championLevel: string championLevel: string;
} }
export interface TopEarnersResponse { export interface TopEarnersResponse {
armies: Army[] armies: Army[];
} }
export interface TopRaidersResponse { export interface TopRaidersResponse {
players: Player[] players: Player[];
}
export interface PlayerResponse {
players: Player[];
} }