Skip to content

Commit

Permalink
adding support for new multi-program backend
Browse files Browse the repository at this point in the history
  • Loading branch information
nibty committed May 21, 2024
1 parent dc232b2 commit 90f9c23
Show file tree
Hide file tree
Showing 17 changed files with 202 additions and 148 deletions.
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
NEXT_PUBLIC_SOL_XEN_PROGRAM_ID=
NEXT_PUBLIC_SOLANA_RPC_ENDPOINT=
NEXT_PUBLIC_API_ENDPOINT=
8 changes: 4 additions & 4 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ body {
rgb(var(--background-start-rgb));

#background-image-overlay {
opacity: 0.3;
opacity: 0.2;
}
[data-theme="dark"],
[data-theme="synthwave"],
Expand All @@ -46,7 +46,7 @@ body {
[data-theme="sunset"],
[data-theme="halloween"] {
#background-image-overlay {
opacity: 0.06;
opacity: 0.12;
}
}
}
Expand All @@ -61,8 +61,8 @@ body {
0% {
opacity: 0;
}
50% {
opacity: 1;
100% {
opacity: 0.90;
}
}

Expand Down
5 changes: 4 additions & 1 deletion app/hooks/LeaderboardDataHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function useLeaderboardData() {
(data: LeaderboardEntry[]) => void,
] = useState<LeaderboardEntry[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isUpdating, setIsUpdating] = useState(true);
const accountType = useAccountType() as AccountType;
const searchParams = useSearchParams();
const [leaderboardIndex, setLeaderboardIndex]: [any, any] = useState<
Expand All @@ -22,11 +23,13 @@ export function useLeaderboardData() {

useEffect(() => {
const fetchData = async () => {
setIsUpdating(true);
fetchLeaderboardData(accountType).then((data: LeaderboardEntry[]) => {
const idxData = generateLeaderboardIndex(data, accountType);
const idxData = generateLeaderboardIndex(data);
setLeaderboardData(data);
setLeaderboardIndex(idxData);
setIsLoading(false);
setIsUpdating(false);
// console.log("Fetched leaderboard data", data, idxData);
});
};
Expand Down
43 changes: 28 additions & 15 deletions app/hooks/SolanaEventsHook.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"use client";
import { useEffect, useRef } from "react";
import {useEffect, useRef, useState} from "react";
import { Connection } from "@solana/web3.js";
import {
AnchorProvider,
BN,
Program,
setProvider,
web3,
} from "@coral-xyz/anchor";
import * as idl from "@/app/leaderboard/target/idl/sol_xen.json";

import {fetchStateData} from "@/app/leaderboard/Api";

interface SolanaEventsContextType {
handleEvent?: (event: EventHash) => void;
}
Expand All @@ -24,38 +25,50 @@ export interface EventHash {
}

export function useSolanaEvents({ handleEvent }: SolanaEventsContextType) {
const [programsIds, setProgramsIds] = useState<string[]>([]);

function useRefEventListener(fn: any) {
const fnRef = useRef(fn);
fnRef.current = fn;
return fnRef;
}

useEffect(() => {
fetchStateData().then((state) => {
setProgramsIds(state.programs);
})
}, []);

// We can use the custom hook declared above
const handleResizeRef = useRefEventListener(handleEvent);

useEffect(() => {
const connection = new Connection(
process.env.NEXT_PUBLIC_SOLANA_RPC_ENDPOINT || "",
);

const provider = new AnchorProvider(connection, null as any);
setProvider(provider);
const programs: Program<any>[] = [];
const listeners: number[] = [];

const idlClone = JSON.parse(JSON.stringify(idl));
idlClone.address = process.env.NEXT_PUBLIC_SOL_XEN_PROGRAM_ID;
const program = new Program(idlClone as any, provider);
for (let i = 0; i < programsIds.length; i++) {
const idlClone = JSON.parse(JSON.stringify(idl));
idlClone.address = programsIds[i];
programs.push(new Program(idlClone as any, provider));

console.log("Listening to hash events");
const listener = program.addEventListener("hashEvent", (event: any) => {
// if (handleResizeRef.current) {
handleResizeRef.current(event);
// }
});
console.log(`Listening to hash events: ${programsIds[i]}`);
listeners[i] = programs[i].addEventListener("hashEvent", (event: EventHash) => {
handleResizeRef.current(event);
});
}

return () => {
console.log("stop listening to hash events");
program.removeEventListener(listener).then();
for (let i = 0; i < programsIds.length; i++) {
console.log(`stop listening to hash events: ${programsIds[i]}`);
programs[i].removeEventListener(listeners[i]).then();
}
};
}, [handleResizeRef]);
}, [programsIds, handleResizeRef]);

return handleResizeRef;
}
3 changes: 2 additions & 1 deletion app/hooks/StateDataHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { State } from "@/app/leaderboard/StateStats";

const initialState: State = {
points: BigInt(0),
solXen: 0,
solXen: 0n,
hashes: 0,
superHashes: 0,
txs: 0,
Expand All @@ -20,6 +20,7 @@ const initialState: State = {
minPriorityFee: 0,
medianPriorityFee: 0,
maxPriorityFee: 0,
programs: []
};

export function useStatsData() {
Expand Down
2 changes: 1 addition & 1 deletion app/leaderboard/AmpBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function AmpBanner({ isLoading, stateData }: AmpBannerProps) {

return (
<div
className={`bg-secondary saturate-50 z-[1] text-secondary-content w-full grid grid-cols-2 sm:grid-cols-3 gap-2 h-[45px] md:h-[50px] opacity-0 ${!isLoading && stateData.amp > 0 ? "fade-in" : ""}`}
className={`bg-info/50 z-[1] text-info-content w-full grid grid-cols-2 sm:grid-cols-3 gap-2 h-[45px] md:h-[50px] opacity-0 ${!isLoading && stateData.amp > 0 ? "fade-in" : ""}`}
>
<div className="border-r place-items-center justify-center py-0 my-0 flex">
<div className="p-0">
Expand Down
57 changes: 48 additions & 9 deletions app/leaderboard/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@ export async function fetchLeaderboardData(accountType: AccountType) {
}

const out = await data.json();

for (const entry of out) {
entry.points = BigInt(entry.points);
if (accountType == AccountType.Solana) {
for (const entry of out) {
entry.solXen = BigInt(entry.solXen);
entry.hashes = BigInt(entry.hashes);
entry.superHashes = BigInt(entry.superHashes);
}
} else {
for (const entry of out) {
entry.points = BigInt(entry.points);
entry.hashes = BigInt(entry.hashes);
entry.superHashes = BigInt(entry.superHashes);
}
}

return out;
Expand All @@ -27,20 +36,20 @@ export async function fetchStateData() {
}

const out = await data.json();
out.points = BigInt(out.points);
out.solXen = BigInt(out.solXen);
out.txs = BigInt(out.txs);
out.hashes = BigInt(out.hashes);
out.superHashes = BigInt(out.superHashes);

return out;
}

export function generateLeaderboardIndex(
leaderboardData: LeaderboardEntry[],
accountType: AccountType | string,
) {
return leaderboardData.reduce(
(acc, entry, index) => {
const accountKey =
accountType == AccountType.Solana ? entry.solAccount : entry.ethAccount;
acc[accountKey] = index;
acc[entry.account] = index;
return acc;
},
{} as Record<string, number>,
Expand All @@ -64,6 +73,12 @@ export async function fetchLeaderboardEntry(account: string) {
if (out?.points) {
out.points = BigInt(out.points);
}
if (out?.solXen) {
out.solXen = BigInt(out.solXen);
}
out.hashes = BigInt(out.hashes);
out.superHashes = BigInt(out.superHashes);

return out;
}

Expand Down Expand Up @@ -122,6 +137,8 @@ export async function fetchAssociatedEthAccounts(

for (const entry of out) {
entry.points = BigInt(entry.points);
entry.hashes = BigInt(entry.hashes);
entry.superHashes = BigInt(entry.superHashes);
}

return out;
Expand All @@ -145,7 +162,29 @@ export async function fetchAssociatedSolAccounts(
const out = await response.json();

for (const entry of out) {
entry.points = BigInt(entry.points);
entry.solXen = BigInt(entry.solXen);
entry.hashes = BigInt(entry.hashes);
entry.superHashes = BigInt(entry.superHashes);
}

return out;
}

export async function fetchStateHistory() {
const data = await fetch(
`${process.env.NEXT_PUBLIC_API_ENDPOINT}/state/history`,
);

if (!data.ok) {
throw new Error("Error fetching state history data");
}

const out = await data.json();

for (const entry of out) {
if (entry.solXen) {
entry.solXen = BigInt(entry.solXen);
}
}

return out;
Expand Down
2 changes: 1 addition & 1 deletion app/leaderboard/Background.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function Background({ isLoading }: BackgroundProps) {
className="fixed h-full w-full left-0 top-0"
>
<Image
className={`opacity-0 ${!isLoading ? "fade-in" : ""}`}
className={`fade-in-animation`}
alt="Background image"
src="/background-image.jpg"
fill
Expand Down
44 changes: 19 additions & 25 deletions app/leaderboard/LeadersTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ interface LeadersTableProps {

export interface LeaderboardEntry {
rank: number;
solAccount: string;
ethAccount: string;
hashes: number;
superHashes: number;
account: string;
hashes: bigint;
superHashes: bigint;
points: bigint;
solXen: number;
solXen: bigint;
}

export function LeadersTable({
Expand All @@ -33,18 +32,19 @@ export function LeadersTable({
const [searchInput, setSearchInput] = useState<string>("");
const changeSearchBox = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchInput(event.target.value);
console.log("Search input", event.target.value);
};

const percentOfState = (points: bigint) => {
if (!stateData) {
const percentOfState = (solXen: bigint): number => {
// @ts-ignore
if (!stateData || stateData.solXen === 0n || stateData.solXen === 0) {
return 0;
}

if (stateData.points === 0n) {
if (solXen === 0n || !solXen) {
return 0;
}
return Math.floor(Number((points * 10000n) / stateData.points) / 100);

return Math.floor(Number((solXen * 10000n) / stateData.solXen) / 100);
};

const handleClickAccount = (account: string) => {
Expand Down Expand Up @@ -111,19 +111,15 @@ export function LeadersTable({
<tbody>
{leaderboardData.map(
(
{ rank, solAccount, ethAccount, hashes, superHashes, points },
{ rank, account, hashes, superHashes, solXen },
index,
) => {
return (
<tr
key={rank}
className={`cursor-pointer hover`}
onClick={() => {
if (accountType == AccountType.Ethereum) {
handleClickAccount(ethAccount);
} else {
handleClickAccount(solAccount);
}
handleClickAccount(account);
}}
>
<td className="p-4 pr-0 border-b border-blue-gray-50">
Expand All @@ -133,7 +129,7 @@ export function LeadersTable({
</td>
<td className="p-4 border-b border-blue-gray-50 text-xs sm:text-base font-mono truncate">
<span>
{accountType == "solana" ? solAccount : ethAccount}
{account}
</span>

<dl className="lg:hidden font-normal mt-2">
Expand All @@ -159,12 +155,12 @@ export function LeadersTable({
solXEN
</dt>
<dd className="text-gray-400 text-sm mt-1 font-mono">
{percentOfState(points) > 0 ? (
{percentOfState(solXen) > 0 ? (
<div className="badge badge-sm badge-success badge-outline mr-2">
{percentOfState(points)}%
{percentOfState(solXen)}%
</div>
) : null}
{solXenValue(points)}
{solXenValue(solXen)}
</dd>
</div>
) : null}
Expand All @@ -183,12 +179,10 @@ export function LeadersTable({
{accountType == AccountType.Solana ? (
<td className="hidden lg:table-cell p-4 border-b border-blue-gray-50">
<span className="font-mono">
{Intl.NumberFormat("en-US").format(
points / 1_000_000_000n,
)}
{percentOfState(points) > 0 ? (
{solXenValue(solXen)}
{percentOfState(solXen) > 0 ? (
<div className="badge badge-sm badge-success badge-outline ml-2">
{percentOfState(points)}%
{percentOfState(solXen)}%
</div>
) : null}
</span>
Expand Down
2 changes: 1 addition & 1 deletion app/leaderboard/StateStat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function StateStat({
if (showDetails) {
return 10;
}
return 50;
return 40;
};

const boarderAlpha = (showDetails: boolean) => {
Expand Down
Loading

0 comments on commit 90f9c23

Please sign in to comment.