Skip to content

Commit

Permalink
feat: add history
Browse files Browse the repository at this point in the history
  • Loading branch information
markjung96 committed Dec 6, 2024
1 parent 6e36080 commit f8ca81b
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 18 deletions.
3 changes: 0 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ export default function HomeWrapper({ searchParams }: { searchParams?: { [key: s
const contractAddress = searchParams?.address;
const isMobile = useMediaQuery("(max-width: 768px)");

console.log("searchParams", searchParams);
console.log("contractAddress", contractAddress);

return (
<div className="h-full flex flex-col items-center justify-center flex-1 px-4 text-center">
{isMobile ? (
Expand Down
117 changes: 117 additions & 0 deletions components/ui/table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import * as React from "react"

import { cn } from "@/src/shared/lib/utils"

const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"

const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"

const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"

const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"

const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"

const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"

const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"

const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"

export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}
30 changes: 15 additions & 15 deletions src/features/verify/api/cairo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { baseUrl } from '.';
import { baseUrl, arbitrumX86BaseUrl } from ".";

type StarknetChainId = '0x534e5f4d41494e' | '0x534e5f5345504f4c4941';
export type StarknetChainId = "0x534e5f4d41494e" | "0x534e5f5345504f4c4941";
export interface CairoVerificationSrcUploadReqDto {
chainId: StarknetChainId;
contractAddress: string;
Expand All @@ -13,23 +13,23 @@ export interface CairoVerificationSrcUploadResultDto {
}

export const postCairoSourceCode = async (
request: CairoVerificationSrcUploadReqDto,
request: CairoVerificationSrcUploadReqDto
): Promise<CairoVerificationSrcUploadResultDto> => {
const formData = new FormData();
formData.append('chainId', request.chainId);
formData.append('contractAddress', request.contractAddress);
formData.append('srcZipFile', request.srcZipFile);
formData.append("chainId", request.chainId);
formData.append("contractAddress", request.contractAddress);
formData.append("srcZipFile", request.srcZipFile);
try {
const response = await fetch(`${baseUrl}/starknet/verifications/sources`, {
method: 'POST',
method: "POST",
body: formData,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
throw new Error('Failed to upload source code');
throw new Error("Failed to upload source code");
}
};

Expand All @@ -52,9 +52,9 @@ export interface CairoVerificationResultDto {
export const verifyCairo = async (request: CairoVerificationReqDto): Promise<CairoVerificationResultDto> => {
try {
const response = await fetch(`${baseUrl}/starknet/verifications`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
body: JSON.stringify(request),
});
Expand All @@ -63,24 +63,24 @@ export const verifyCairo = async (request: CairoVerificationReqDto): Promise<Cai
}
return await response.json();
} catch (error) {
throw new Error('Failed to verify contract');
throw new Error("Failed to verify contract");
}
};

export const getCairoVerificationResult = async (
chainId: StarknetChainId,
contractAddress: string,
contractAddress: string
): Promise<CairoVerificationResultDto> => {
try {
const response = await fetch(
`${baseUrl}/starknet/verifications?chainId=${chainId}&contractAddress=${contractAddress}`,
{ headers: { 'Cache-Control': 'no-cache' } },
`${arbitrumX86BaseUrl}/starknet/verifications?chainId=${chainId}&contractAddress=${contractAddress}`,
{ headers: { "Cache-Control": "no-cache" } }
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
throw new Error('Failed to get verification result');
throw new Error("Failed to get verification result");
}
};
16 changes: 16 additions & 0 deletions src/shared/lib/network.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StarknetChainId } from "@/src/features/verify/api";
import { isValidHexAddress, type Hex } from "@metamask/utils";

/**
Expand All @@ -22,3 +23,18 @@ export const isStarknetAddressOrHash = (address: string): boolean => {

return starknetAddressRegex.test(address) || starknetHashRegex.test(address);
};

type ChainId = StarknetChainId;
export const getChainAndNetworkByChainId = (chainId: ChainId): { chain: string; network: string } => {
switch (chainId) {
case "0x534e5f4d41494e":
return { chain: "starknet", network: "mainnet" };
case "0x534e5f5345504f4c4941":
return { chain: "starknet", network: "sepolia" };
default:
return { chain: "Unknown", network: "Unknown" };
}
};

export const shortenAddress = (address: string, length = 6): string =>
`${address.slice(0, length)}...${address.slice(-length)}`;
126 changes: 126 additions & 0 deletions src/widgets/Landing/ui/contract-history.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { arbitrumX86BaseUrl } from "@/src/features/verify/api";
import { getChainAndNetworkByChainId, shortenAddress } from "@/src/shared/lib/network";
import { Loader } from "@/src/widgets/Loader";
import { Button } from "@/src/shared/ui";

interface ContractHistory {
id: number;
chainId: string;
contractAddress: string;
classHash: string;
compiledClassHash: string;
declareTxHash: string;
scarbVersion: string;
compileTimestamp: number;
verifyRequestAddress: string | null;
verifiedTimestamp: number;
createdAt: string;
updatedAt: string;
}

const ITEMS_PER_PAGE = 5;

export const ContractHistory = () => {
const [history, setHistory] = useState<ContractHistory[]>([]);
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
const router = useRouter();

const totalPages = Math.ceil(history.length / ITEMS_PER_PAGE);

const getStarknetContractHistory = async () => {
setLoading(true);
try {
const response = await fetch(`${arbitrumX86BaseUrl}/starknet/verifications/history`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setHistory(result);
} catch (error) {
throw new Error("Failed to get contract history");
} finally {
setLoading(false);
}
};

const handlePageChange = (page: number) => {
setCurrentPage(page);
window.scrollTo(0, 0); // 페이지 전환 시 스크롤 초기화
};

const paginatedData = history.slice((currentPage - 1) * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE);

useEffect(() => {
getStarknetContractHistory();
}, []);

return loading ? (
<div className="my-20 p-4 w-[696px] flex justify-center">
<Loader size={24} />
</div>
) : (
<div className="my-20 p-4 border rounded-sm">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Chain</TableHead>
<TableHead className="w-[100px]">Network</TableHead>
<TableHead>Contract Address</TableHead>
<TableHead>Scrab Version</TableHead>
<TableHead className="text-right">Verified at</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{paginatedData.map((item, index) => {
const { chain, network } = getChainAndNetworkByChainId(item.chainId as any);

const handleContractAddressOnClick = () => {
router.push(`/verify?chain=${chain}&network=${network}&contractAddress=${item.contractAddress}`);
};

return (
<TableRow key={index} className="cursor-pointer hover:opacity-70" onClick={handleContractAddressOnClick}>
<TableCell className="text-left capitalize">{chain}</TableCell>
<TableCell className="capitalize">{network}</TableCell>
<TableCell className="underline">{shortenAddress(item.contractAddress, 12)}</TableCell>
<TableCell>{item.scarbVersion}</TableCell>
<TableCell className="text-right">{item.verifiedTimestamp}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>

<div className="flex justify-between items-center mt-4">
<Button variant="ghost" onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1}>
Previous
</Button>
<span className="text-sm text-gray-500 ">
Page {currentPage} of {totalPages}
</span>
<Button variant="ghost" onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages}>
Next
</Button>
</div>
</div>
);
};

0 comments on commit f8ca81b

Please sign in to comment.