From 18e88dc456aeba3a58d8271a5262ac610a9c294e Mon Sep 17 00:00:00 2001 From: motechFR Date: Thu, 24 Oct 2024 23:47:32 +0300 Subject: [PATCH] Cleanup the admin dashboard --- .../components/common/SiteNavigation.tsx | 4 +- .../components/contract/ContractDashboard.tsx | 137 ++++++++++++++---- .../lib/contract/getContractData.ts | 23 ++- .../clients/builderNFTSeasonOneClient.ts | 36 +++++ 4 files changed, 166 insertions(+), 34 deletions(-) diff --git a/apps/scoutgameadmin/components/common/SiteNavigation.tsx b/apps/scoutgameadmin/components/common/SiteNavigation.tsx index fa5d3f41b2..ceeb1fe577 100644 --- a/apps/scoutgameadmin/components/common/SiteNavigation.tsx +++ b/apps/scoutgameadmin/components/common/SiteNavigation.tsx @@ -5,7 +5,7 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { FaGithubAlt } from 'react-icons/fa'; import { HiOutlineUsers } from 'react-icons/hi2'; -import { MdLaunch } from 'react-icons/md'; +import { MdDocumentScanner } from 'react-icons/md'; const StyledBottomNavigation = styled(BottomNavigation, { shouldForwardProp: (prop) => prop !== 'topNav' @@ -56,7 +56,7 @@ export function SiteNavigation({ topNav, isAuthenticated = false }: { topNav?: b label='Contract' href='/contract' value='contract' - icon={} + icon={} LinkComponent={Link} /> diff --git a/apps/scoutgameadmin/components/contract/ContractDashboard.tsx b/apps/scoutgameadmin/components/contract/ContractDashboard.tsx index 20092eae98..9cf0594e6f 100644 --- a/apps/scoutgameadmin/components/contract/ContractDashboard.tsx +++ b/apps/scoutgameadmin/components/contract/ContractDashboard.tsx @@ -1,39 +1,118 @@ -import { Grid2 } from '@mui/material'; +import { Box, Divider, Grid2, IconButton, Typography } from '@mui/material'; import Link from 'next/link'; +import { MdLaunch } from 'react-icons/md'; import type { BuilderNFTContractData } from 'lib/contract/getContractData'; +function ContractLink({ + address, + linkType = 'address', + title, + subtitle +}: { + address: string; + linkType?: 'address' | 'token' | 'contract'; + title: string; + subtitle?: string; +}) { + return ( + + {title} + {subtitle && {subtitle}} + + {address} + + + + + + ); +} + +function SectionTitle({ title }: { title: string }) { + return ( + + {title} + + ); +} + +function GridDivider() { + return ( + + + + ); +} + export function ContractDashboard(data: BuilderNFTContractData) { + const itemSize = { xs: 12, md: 6 }; + return ( - - -

Proxy address

- - {data.contractAddress} - -
- -

Current Admin

- - {data.currentAdmin} - -
- -

Current Implementation

- - {data.currentImplementation} - -
- -

Proceeds Receiver

- - {data.proceedsReceiver} - + + + - - -

Registered builders

-

{data.totalSupply.toString()}

+ + + + + + + + + + + + + Registered builder NFTs + + + {data.totalSupply.toString()} + + + + {/* Currently, this is the balance of the proceeds receiver wallet. Once we start moving funds, we should look at logs instead */} + + Sales + + + {Number(data.receiverUsdcBalance).toLocaleString('en-US')} USD + + + + + + + + + + + + + +
); diff --git a/apps/scoutgameadmin/lib/contract/getContractData.ts b/apps/scoutgameadmin/lib/contract/getContractData.ts index 12f1a08ec9..eb0c346250 100644 --- a/apps/scoutgameadmin/lib/contract/getContractData.ts +++ b/apps/scoutgameadmin/lib/contract/getContractData.ts @@ -1,29 +1,46 @@ +import { getPublicClient } from '@packages/onchain/getPublicClient'; import { builderContractReadonlyApiClient } from '@packages/scoutgame/builderNfts/clients/builderContractReadClient'; import { builderProxyContractReadonlyApiClient } from '@packages/scoutgame/builderNfts/clients/builderProxyContractReadClient'; -import { getBuilderContractAddress } from '@packages/scoutgame/builderNfts/constants'; +import { + getBuilderContractAddress, + usdcOptimismMainnetContractAddress +} from '@packages/scoutgame/builderNfts/constants'; +import { UsdcErc20ABIClient } from '@packages/scoutgame/builderNfts/usdcContractApiClient'; import type { Address } from 'viem'; +import { optimism } from 'viem/chains'; export type BuilderNFTContractData = { currentAdmin: Address; + currentMinter: Address; currentImplementation: Address; proceedsReceiver: Address; totalSupply: bigint; contractAddress: Address; + receiverUsdcBalance: number; }; export async function getContractData(): Promise { - const [currentAdmin, currentImplementation, proceedsReceiver, totalSupply] = await Promise.all([ + const [currentAdmin, currentMinter, currentImplementation, proceedsReceiver, totalSupply] = await Promise.all([ builderProxyContractReadonlyApiClient.admin(), + builderContractReadonlyApiClient.getMinter(), builderProxyContractReadonlyApiClient.implementation(), builderProxyContractReadonlyApiClient.getProceedsReceiver(), builderContractReadonlyApiClient.totalBuilderTokens() ]); + const balance = await new UsdcErc20ABIClient({ + chain: optimism, + publicClient: getPublicClient(optimism.id), + contractAddress: usdcOptimismMainnetContractAddress + }).balanceOf({ args: { account: proceedsReceiver } }); + return { currentAdmin: currentAdmin as Address, + currentMinter: currentMinter as Address, currentImplementation: currentImplementation as Address, proceedsReceiver: proceedsReceiver as Address, totalSupply, - contractAddress: getBuilderContractAddress() + contractAddress: getBuilderContractAddress(), + receiverUsdcBalance: Number(balance / BigInt(1e6)) }; } diff --git a/packages/scoutgame/src/builderNfts/clients/builderNFTSeasonOneClient.ts b/packages/scoutgame/src/builderNfts/clients/builderNFTSeasonOneClient.ts index c6c432fd0f..842d8e8144 100644 --- a/packages/scoutgame/src/builderNfts/clients/builderNFTSeasonOneClient.ts +++ b/packages/scoutgame/src/builderNfts/clients/builderNFTSeasonOneClient.ts @@ -242,6 +242,19 @@ export class BuilderNFTSeasonOneImplementation01Client { ], stateMutability: 'view', type: 'function' + }, + { + inputs: [], + name: 'getMinter', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' } ]; @@ -325,6 +338,29 @@ export class BuilderNFTSeasonOneImplementation01Client { return result as string; } + async getMinter(): Promise { + const txData = encodeFunctionData({ + abi: this.abi, + functionName: 'getMinter', + args: [] + }); + + const { data } = await this.publicClient.call({ + to: this.contractAddress, + data: txData + }); + + // Decode the result based on the expected return type + const result = decodeFunctionResult({ + abi: this.abi, + functionName: 'getMinter', + data: data as `0x${string}` + }); + + // Parse the result based on the return type + return result as string; + } + async getPriceIncrement(): Promise { const txData = encodeFunctionData({ abi: this.abi,