forked from mico/idle_moloch
Compare commits
2 Commits
session-wa
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| eb4ce2d2c7 | |||
|
|
3332eaf81e |
@ -1,24 +1,33 @@
|
|||||||
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) {
|
||||||
player {
|
player {
|
||||||
id
|
id
|
||||||
@ -32,83 +41,177 @@ 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) {
|
||||||
id
|
id
|
||||||
numberOfRaids
|
numberOfRaids
|
||||||
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 });
|
||||||
|
|
||||||
return (
|
// Fetch player prestige
|
||||||
<div className={styles.leaderboard}>
|
const playerPrestigeResponse = await fetch(SUBGRAPH_URL, {
|
||||||
<h2 className={styles.title}>Leaderboard</h2>
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
<div className={styles.tabs}>
|
body: JSON.stringify({
|
||||||
<button
|
query: `{
|
||||||
className={`${styles.tab} ${activeTab === 'earners' ? styles.active : ''}`}
|
players(first: 10, orderBy: prestigeLevel, orderDirection: desc) {
|
||||||
onClick={() => setActiveTab('earners')}
|
id
|
||||||
>
|
prestigeLevel
|
||||||
Top Earners
|
}
|
||||||
</button>
|
}`,
|
||||||
<button
|
}),
|
||||||
className={`${styles.tab} ${activeTab === 'raiders' ? styles.active : ''}`}
|
});
|
||||||
onClick={() => setActiveTab('raiders')}
|
const playerPrestigeData = await playerPrestigeResponse.json();
|
||||||
>
|
setPlayerPrestige({ players: playerPrestigeData.data.players });
|
||||||
Top Raiders
|
} catch (error) {
|
||||||
</button>
|
console.error("Error fetching leaderboard:", error);
|
||||||
</div>
|
}
|
||||||
|
};
|
||||||
|
|
||||||
{activeTab === 'earners' && (
|
fetchLeaderboards();
|
||||||
<div className={styles.list}>
|
const interval = setInterval(fetchLeaderboards, 30000); // Refresh every 30 seconds
|
||||||
{topEarners?.armies.map((army, index) => (
|
return () => clearInterval(interval);
|
||||||
<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>
|
return (
|
||||||
<span className={styles.stat}>{formatUnits(BigInt(army.profitPerSecond), 4)} GELD/s</span>
|
<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>
|
||||||
|
<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>
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeTab === 'raiders' && (
|
{activeTab === "earners" && (
|
||||||
<div className={styles.list}>
|
<div className={styles.list}>
|
||||||
{topRaiders?.players.map((player, index) => (
|
{topEarners?.armies.map((army, index) => (
|
||||||
<div key={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}>{player.id.slice(0, 6)}...{player.id.slice(-4)}</span>
|
<span className={styles.address}>
|
||||||
<span className={styles.stat}>{player.numberOfRaids} raids</span>
|
{army.player.id.slice(0, 6)}...
|
||||||
</div>
|
{army.player.id.slice(-4)}
|
||||||
))}
|
</span>
|
||||||
</div>
|
<span className={styles.stat}>
|
||||||
)}
|
{formatUnits(BigInt(army.profitPerSecond), 4)}{" "}
|
||||||
</div>
|
GELD/s
|
||||||
)
|
</span>
|
||||||
}
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
export default Leaderboard
|
{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>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Leaderboard;
|
||||||
|
|||||||
@ -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 {
|
}
|
||||||
player: Player
|
|
||||||
profitPerSecond: string
|
export interface Army {
|
||||||
projectedDailyEarnings: string
|
player: Player;
|
||||||
molochDenierLevel: string
|
profitPerSecond: string;
|
||||||
apprenticeLevel: string
|
projectedDailyEarnings: string;
|
||||||
anointedLevel: string
|
molochDenierLevel: string;
|
||||||
championLevel: string
|
apprenticeLevel: string;
|
||||||
}
|
anointedLevel: string;
|
||||||
|
championLevel: string;
|
||||||
export interface TopEarnersResponse {
|
}
|
||||||
armies: Army[]
|
|
||||||
}
|
export interface TopEarnersResponse {
|
||||||
|
armies: Army[];
|
||||||
export interface TopRaidersResponse {
|
}
|
||||||
players: Player[]
|
|
||||||
}
|
export interface TopRaidersResponse {
|
||||||
|
players: Player[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlayerResponse {
|
||||||
|
players: Player[];
|
||||||
|
}
|
||||||
|
|||||||
@ -25,8 +25,6 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
mapping(address => Boss) private bosses;
|
mapping(address => Boss) private bosses;
|
||||||
mapping(address => LastBossResult) private lastBossResults;
|
mapping(address => LastBossResult) private lastBossResults;
|
||||||
|
|
||||||
mapping(address => address) private sessionWallets;
|
|
||||||
|
|
||||||
// WETH
|
// WETH
|
||||||
IWETH public immutable weth = IWETH(WETH);
|
IWETH public immutable weth = IWETH(WETH);
|
||||||
// RGCVII token
|
// RGCVII token
|
||||||
@ -94,10 +92,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modifier onlyActiveSession() {
|
modifier onlyActiveSession() {
|
||||||
address delegatedPlayer = sessionWallets[msg.sender];
|
require(players[msg.sender].has_active_session, "Session is not active, you need to buy into the game first");
|
||||||
require(
|
|
||||||
players[msg.sender].has_active_session ||
|
|
||||||
players[delegatedPlayer].has_active_session, "Session is not active, you need to buy into the game first");
|
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,9 +200,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
|
|
||||||
// Manual minting for itchy fingers
|
// Manual minting for itchy fingers
|
||||||
function raid() external onlyActiveSession {
|
function raid() external onlyActiveSession {
|
||||||
address delegatedPlayer = sessionWallets[msg.sender];
|
performRaid(msg.sender);
|
||||||
address player = delegatedPlayer == address(0) ? msg.sender : delegatedPlayer;
|
|
||||||
performRaid(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper so we can use it when buying units too
|
// Helper so we can use it when buying units too
|
||||||
@ -247,11 +240,9 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
|
|
||||||
// Add a unit to your army
|
// Add a unit to your army
|
||||||
function addUnit(uint8 unit, uint16 n_units) external onlyActiveSession {
|
function addUnit(uint8 unit, uint16 n_units) external onlyActiveSession {
|
||||||
address delegatedPlayer = sessionWallets[msg.sender];
|
|
||||||
address player = delegatedPlayer == address(0) ? msg.sender : delegatedPlayer;
|
|
||||||
require(unit <= 3, "Unknown unit");
|
require(unit <= 3, "Unknown unit");
|
||||||
|
|
||||||
Army storage army = armies[player];
|
Army storage army = armies[msg.sender];
|
||||||
uint16 currentLevel = 0;
|
uint16 currentLevel = 0;
|
||||||
if (unit == 0) {
|
if (unit == 0) {
|
||||||
// moloch_denier
|
// moloch_denier
|
||||||
@ -270,13 +261,13 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
uint256 cost = RaidGeldUtils.calculateUnitPrice(unit, currentLevel, n_units);
|
uint256 cost = RaidGeldUtils.calculateUnitPrice(unit, currentLevel, n_units);
|
||||||
|
|
||||||
|
|
||||||
performRaid(player);
|
performRaid(msg.sender);
|
||||||
|
|
||||||
// TODO: Since we are first minting then burning the token, this could be simplified
|
// TODO: Since we are first minting then burning the token, this could be simplified
|
||||||
require(balanceOf(player) >= cost, "Not enough GELD to add this unit");
|
require(balanceOf(msg.sender) >= cost, "Not enough GELD to add this unit");
|
||||||
|
|
||||||
// then burn the cost of the new army
|
// then burn the cost of the new army
|
||||||
_burn(player, cost);
|
_burn(msg.sender, cost);
|
||||||
|
|
||||||
// Increase level
|
// Increase level
|
||||||
if (unit == 0) {
|
if (unit == 0) {
|
||||||
@ -298,7 +289,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
|
|
||||||
// Emit event
|
// Emit event
|
||||||
emit UnitAdded(
|
emit UnitAdded(
|
||||||
player,
|
msg.sender,
|
||||||
unit,
|
unit,
|
||||||
n_units,
|
n_units,
|
||||||
cost,
|
cost,
|
||||||
@ -437,12 +428,6 @@ contract RaidGeld is ERC20, Ownable, Constants {
|
|||||||
DAO = _dao;
|
DAO = _dao;
|
||||||
}
|
}
|
||||||
|
|
||||||
function approveSessionWallet(address _wallet) payable external onlyPlayer {
|
|
||||||
require(!isRegistered(_wallet), "Wallet belongs to other player");
|
|
||||||
sessionWallets[msg.sender] = _wallet;
|
|
||||||
payable(_wallet).call{value: msg.value}("");
|
|
||||||
}
|
|
||||||
|
|
||||||
receive() external payable {
|
receive() external payable {
|
||||||
revert("No plain Ether accepted, use register() function to check in :)");
|
revert("No plain Ether accepted, use register() function to check in :)");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user