Skip to content

Commit

Permalink
Cleanup the admin dashboard (#4891)
Browse files Browse the repository at this point in the history
* Cleanup the admin dashboard

* Remove bolding on the titles
  • Loading branch information
motechFR authored Oct 25, 2024
1 parent 6269a8f commit d60e119
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 34 deletions.
4 changes: 2 additions & 2 deletions apps/scoutgameadmin/components/common/SiteNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -56,7 +56,7 @@ export function SiteNavigation({ topNav, isAuthenticated = false }: { topNav?: b
label='Contract'
href='/contract'
value='contract'
icon={<MdLaunch size='24px' />}
icon={<MdDocumentScanner size='24px' />}
LinkComponent={Link}
/>
</StyledBottomNavigation>
Expand Down
133 changes: 104 additions & 29 deletions apps/scoutgameadmin/components/contract/ContractDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,114 @@
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 (
<Box gap={1} display='flex' flexDirection='column'>
<Typography variant='h6'>{title}</Typography>
<Box sx={{ minHeight: '40px' }}>{subtitle && <Typography variant='body2'>{subtitle}</Typography>}</Box>
<Link href={`https://optimism.blockscout.com/${linkType}/${address}`} target='_blank'>
{address}
<IconButton size='small' color='primary'>
<MdLaunch size='16px' />
</IconButton>
</Link>
</Box>
);
}

function SectionTitle({ title }: { title: string }) {
return (
<Typography variant='h5' fontWeight='bold'>
{title}
</Typography>
);
}

function GridDivider() {
return (
<Grid2 size={12}>
<Divider />
</Grid2>
);
}

export function ContractDashboard(data: BuilderNFTContractData) {
const itemSize = { xs: 12, md: 6 };

return (
<Grid2 container gap={2}>
<Grid2 size={{ xs: 12 }}>
<h2>Proxy address</h2>
<Link href={`https://optimism.blockscout.com/token/${data.contractAddress}`} target='_blank'>
{data.contractAddress}
</Link>
</Grid2>
<Grid2 size={{ xs: 12 }}>
<h2>Current Admin</h2>
<Link href={`https://optimism.blockscout.com/address/${data.currentAdmin}`} target='_blank'>
{data.currentAdmin}
</Link>
</Grid2>
<Grid2 size={{ xs: 12 }}>
<h2>Current Implementation</h2>
<Link href={`https://optimism.blockscout.com/address/${data.currentImplementation}`} target='_blank'>
{data.currentImplementation}
</Link>
</Grid2>
<Grid2 size={{ xs: 12 }}>
<h2>Proceeds Receiver</h2>
<Link href={`https://optimism.blockscout.com/address/${data.proceedsReceiver}`} target='_blank'>
{data.proceedsReceiver}
</Link>
<Grid2 container spacing={2} px={6}>
<Grid2 size={12}>
<SectionTitle title='Contract Addresses' />
</Grid2>

<Grid2 size={{ xs: 12 }}>
<h2>Registered builders</h2>
<p>{data.totalSupply.toString()}</p>
<Grid2 size={itemSize}>
<ContractLink
address={data.contractAddress}
title='Proxy address'
linkType='token'
subtitle='Season-long contract holding the data about the minted NFTs, which delegates minting to an implementation contract.'
/>
</Grid2>
<Grid2 size={itemSize}>
<ContractLink
address={data.currentImplementation}
title='Current Implementation'
subtitle='This contract is called by the proxy and handles the minting logic. We upgrade to a new implementation multiple times over the season.'
/>
</Grid2>
<GridDivider />
<Grid2 size={12}>
<SectionTitle title='Data' />
</Grid2>
<Grid2 size={{ xs: 12, md: 4 }}>
<Typography variant='h6'>Registered builder NFTs</Typography>
<Typography variant='body1' fontWeight='bold'>
{data.totalSupply.toString()}
</Typography>
</Grid2>
<Grid2 size={{ xs: 12, md: 4 }}>
{/* Currently, this is the balance of the proceeds receiver wallet. Once we start moving funds, we should look at logs instead */}
<Typography variant='h6'>Sales</Typography>
<Typography variant='body1' fontWeight='bold'>
{Number(data.receiverUsdcBalance).toLocaleString('en-US')} USD
</Typography>
</Grid2>
<GridDivider />
<Grid2 size={12}>
<SectionTitle title='Roles & Permissions' />
</Grid2>
<Grid2 size={itemSize}>
<ContractLink
address={data.currentAdmin}
title='Admin'
subtitle='Admin wallet can upgrade the contract, update the wallet that receives proceeds from NFT sales, modify pricing, register builders and mint tokens.'
/>
</Grid2>
<Grid2 size={itemSize}>
<ContractLink
address={data.currentMinter}
title='Minter'
subtitle='Minter wallet can register new builder nfts and mint tokens to any address.'
/>
</Grid2>
<Grid2 size={itemSize}>
<ContractLink
address={data.proceedsReceiver}
title='Proceeds Receiver'
subtitle='This is the wallet address that receives funds paid to mint builder NFTs.'
/>
</Grid2>
</Grid2>
);
Expand Down
23 changes: 20 additions & 3 deletions apps/scoutgameadmin/lib/contract/getContractData.ts
Original file line number Diff line number Diff line change
@@ -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<BuilderNFTContractData> {
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))
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,19 @@ export class BuilderNFTSeasonOneImplementation01Client {
],
stateMutability: 'view',
type: 'function'
},
{
inputs: [],
name: 'getMinter',
outputs: [
{
internalType: 'address',
name: '',
type: 'address'
}
],
stateMutability: 'view',
type: 'function'
}
];

Expand Down Expand Up @@ -325,6 +338,29 @@ export class BuilderNFTSeasonOneImplementation01Client {
return result as string;
}

async getMinter(): Promise<string> {
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<bigint> {
const txData = encodeFunctionData({
abi: this.abi,
Expand Down

0 comments on commit d60e119

Please sign in to comment.