diff --git a/CHANGELOG.md b/CHANGELOG.md index ea95d52ad..788bc8960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#1213](https://github.com/alleslabs/celatone-frontend/pull/1213) EVM verification status modal, and highlight bar - [#1211](https://github.com/alleslabs/celatone-frontend/pull/1211) Implement evm gas refund logic - [#1210](https://github.com/alleslabs/celatone-frontend/pull/1210) Add EVM contract details code preview - [#1209](https://github.com/alleslabs/celatone-frontend/pull/1209) Implement evm contract details interaction diff --git a/src/lib/components/evm-verify-section/FailedDetails.tsx b/src/lib/components/evm-verify-section/FailedDetails.tsx new file mode 100644 index 000000000..bba6febd9 --- /dev/null +++ b/src/lib/components/evm-verify-section/FailedDetails.tsx @@ -0,0 +1,72 @@ +import { Button, HStack, Text } from "@chakra-ui/react"; + +import { useInternalNavigate, useMobile } from "lib/app-provider"; +import { EvmVerifyInfo } from "lib/services/types"; +import type { HexAddr20 } from "lib/types"; +import { formatUTC } from "lib/utils"; +import { EvmVerifyStatusModalWithTrigger } from "../modal/evm-verify-status"; + +interface FailedDetailsProps { + contractAddress: HexAddr20; + evmVerifyInfo: EvmVerifyInfo; +} + +export const FailedDetails = ({ + contractAddress, + evmVerifyInfo, +}: FailedDetailsProps) => { + const isMobile = useMobile(); + const navigate = useInternalNavigate(); + + const handleNavigate = () => + navigate({ + pathname: "/evm-contracts/verify", + query: { contractAddress }, + }); + + if (isMobile) + return ( + <> + + Verification failed: Verification was submitted on{" "} + {formatUTC(evmVerifyInfo.submittedTimestamp)} but an error occurred. + + + If you are the owner, you can reverify this contract on the desktop + interface. + + + Verify Details + + } + /> + + ); + + return ( + <> + + Verification failed: Verification was submitted on{" "} + {formatUTC(evmVerifyInfo.submittedTimestamp)} but an error occurred. + + + + Verify Details + + } + /> + + + + ); +}; diff --git a/src/lib/components/evm-verify-section/InProgressDetails.tsx b/src/lib/components/evm-verify-section/InProgressDetails.tsx new file mode 100644 index 000000000..364822155 --- /dev/null +++ b/src/lib/components/evm-verify-section/InProgressDetails.tsx @@ -0,0 +1,31 @@ +import { Button, Text } from "@chakra-ui/react"; + +import type { HexAddr20 } from "lib/types"; +import { EvmVerifyInfo } from "lib/services/types"; +import { EvmVerifyStatusModalWithTrigger } from "../modal/evm-verify-status"; + +interface InProgressDetailsProps { + contractAddress: HexAddr20; + evmVerifyInfo: EvmVerifyInfo; +} + +export const InProgressDetails = ({ + contractAddress, + evmVerifyInfo, +}: InProgressDetailsProps) => ( + <> + + Contract verification is in progress and may take several hours depending + on contract complexity. + + + View Verification Details + + } + /> + +); diff --git a/src/lib/components/evm-verify-section/NotVerifiedDetails.tsx b/src/lib/components/evm-verify-section/NotVerifiedDetails.tsx index 9337d959d..ca8dd3583 100644 --- a/src/lib/components/evm-verify-section/NotVerifiedDetails.tsx +++ b/src/lib/components/evm-verify-section/NotVerifiedDetails.tsx @@ -1,4 +1,4 @@ -import { Button, Flex, Text } from "@chakra-ui/react"; +import { Button, Text } from "@chakra-ui/react"; import { useInternalNavigate, useMobile } from "lib/app-provider"; import type { HexAddr20 } from "lib/types"; @@ -19,51 +19,41 @@ export const NotVerifiedDetails = ({ query: { contractAddress }, }); + if (isMobile) + return ( + <> + + This contract has not been verified. If you are the owner, you can + verify it to allow other users to view the source code + + + Verification is only currently supported on desktop. + + + ); + return ( - - {isMobile ? ( - <> - - This contract has not been verified. If you are the owner, you can - verify it to allow other users to view the source code - - - Verification is only currently supported on desktop. - - - ) : ( - <> - - This contract has not been verified. If you are the owner, you can{" "} - - verify it - {" "} - to allow other users to view the source code - - - - )} - + <> + + This contract has not been verified. If you are the owner, you can{" "} + + verify it + {" "} + to allow other users to view the source code + + + ); }; diff --git a/src/lib/components/evm-verify-section/VerifiedDetails.tsx b/src/lib/components/evm-verify-section/VerifiedDetails.tsx new file mode 100644 index 000000000..dddb8d83e --- /dev/null +++ b/src/lib/components/evm-verify-section/VerifiedDetails.tsx @@ -0,0 +1,29 @@ +import { Text } from "@chakra-ui/react"; + +import { TabIndex } from "lib/pages/evm-contract-details/types"; +import type { HexAddr20 } from "lib/types"; +import { AppLink } from "../AppLink"; + +interface VerifiedDetailsProps { + contractAddress: HexAddr20; +} + +export const VerifiedDetails = ({ contractAddress }: VerifiedDetailsProps) => ( + + This contract source code is verified with the exact match. You can view its{" "} + + + Source Code. + + + +); diff --git a/src/lib/components/evm-verify-section/index.ts b/src/lib/components/evm-verify-section/index.ts deleted file mode 100644 index c97512379..000000000 --- a/src/lib/components/evm-verify-section/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./NotVerifiedDetails"; diff --git a/src/lib/components/evm-verify-section/index.tsx b/src/lib/components/evm-verify-section/index.tsx new file mode 100644 index 000000000..7dfed134e --- /dev/null +++ b/src/lib/components/evm-verify-section/index.tsx @@ -0,0 +1,65 @@ +import { Flex } from "@chakra-ui/react"; + +import type { HexAddr20, Option } from "lib/types"; + +import { NotVerifiedDetails } from "./NotVerifiedDetails"; +import { EvmVerifyInfo } from "lib/services/types"; +import { useMobile } from "lib/app-provider"; +import { VerifiedDetails } from "./VerifiedDetails"; +import { FailedDetails } from "./FailedDetails"; +import { InProgressDetails } from "./InProgressDetails"; + +interface EvmVerifySectionProps { + contractAddress: HexAddr20; + evmVerifyInfo: Option; +} + +const EvmVerifySectionBody = ({ + contractAddress, + evmVerifyInfo, +}: EvmVerifySectionProps) => { + if (!evmVerifyInfo) { + return ; + } + + const { errorMessage, isVerified } = evmVerifyInfo; + + if (errorMessage) { + return ( + + ); + } + + if (isVerified) { + return ; + } + + return ( + + ); +}; + +export const EvmVerifySection = (props: EvmVerifySectionProps) => { + const isMobile = useMobile(); + + return ( + + + + ); +}; diff --git a/src/lib/components/modal/evm-verify-status/EvmVerifyAlert.tsx b/src/lib/components/modal/evm-verify-status/EvmVerifyAlert.tsx new file mode 100644 index 000000000..fa93fd8b2 --- /dev/null +++ b/src/lib/components/modal/evm-verify-status/EvmVerifyAlert.tsx @@ -0,0 +1,38 @@ +import { Alert, AlertDescription, Flex, Text } from "@chakra-ui/react"; + +import { CustomIcon } from "lib/components/icon"; + +interface EvmVerifyAlertProps { + errorMsg: string; +} +export const EvmVerifyAlert = ({ errorMsg }: EvmVerifyAlertProps) => ( + + + + + + Verification failed + + + {errorMsg} + + + + +); diff --git a/src/lib/components/modal/evm-verify-status/EvmVerifyProcess.tsx b/src/lib/components/modal/evm-verify-status/EvmVerifyProcess.tsx new file mode 100644 index 000000000..417500adb --- /dev/null +++ b/src/lib/components/modal/evm-verify-status/EvmVerifyProcess.tsx @@ -0,0 +1,70 @@ +import { Box, Flex, Text } from "@chakra-ui/react"; +import { useMobile } from "lib/app-provider"; +import { EvmVerifyInfo } from "lib/services/types"; +import { getProcessSteps } from "./utils"; +import { formatUTC } from "lib/utils"; +import { EvmVerifyProcessStepIcon } from "./EvmVerifyProcessStepIcon"; + +interface EvmVerifyProcessProps { + evmVerifyInfo: EvmVerifyInfo; +} + +export const EvmVerifyProcess = ({ evmVerifyInfo }: EvmVerifyProcessProps) => { + const isMobile = useMobile(); + const steps = getProcessSteps(evmVerifyInfo); + + return ( + + + Verification Process + + + {steps.map((step, index) => ( + + + + + {index < steps.length - 1 && ( + + )} + + + + {step.label} + + {isMobile && step.timestamp && ( + + {formatUTC(step.timestamp)} + + )} + {step.errorMsg && ( + + {step.errorMsg} + + )} + + + + + {!isMobile && step.timestamp && ( + + {formatUTC(step.timestamp)} + + )} + {step.state === "In Progress" && ( + + {step.state} + + )} + + + ))} + + + ); +}; diff --git a/src/lib/components/modal/evm-verify-status/EvmVerifyProcessStepIcon.tsx b/src/lib/components/modal/evm-verify-status/EvmVerifyProcessStepIcon.tsx new file mode 100644 index 000000000..e4ebbbfbf --- /dev/null +++ b/src/lib/components/modal/evm-verify-status/EvmVerifyProcessStepIcon.tsx @@ -0,0 +1,41 @@ +import { Box, Flex } from "@chakra-ui/react"; + +import { CustomIcon } from "lib/components/icon"; + +export const EvmVerifyProcessStepIcon = ({ state }: { state: string }) => { + switch (state) { + case "Failed": + return ; + case "Completed": + return ; + case "In Progress": + return ( + + ); + case "Pending": + default: + return ( + + ); + } +}; diff --git a/src/lib/components/modal/evm-verify-status/EvmVerifyRequestInfo.tsx b/src/lib/components/modal/evm-verify-status/EvmVerifyRequestInfo.tsx new file mode 100644 index 000000000..9a791f461 --- /dev/null +++ b/src/lib/components/modal/evm-verify-status/EvmVerifyRequestInfo.tsx @@ -0,0 +1,90 @@ +import type { HexAddr20, Option } from "lib/types"; +import { EvmVerifyInfo } from "lib/services/types"; +import { Flex, HStack } from "@chakra-ui/react"; +import { Divider, Text, TextProps } from "@chakra-ui/react"; +import { ExplorerLink } from "lib/components/ExplorerLink"; +import { formatUTC, getLicenseTypeLabel } from "lib/utils"; +import { useMobile } from "lib/app-provider"; + +const baseTextStyle: TextProps = { + color: "text.dark", + variant: "body2", + whiteSpace: "nowrap", +}; + +interface EvmVerifyRequestInfoProps { + contractAddress: HexAddr20; + evmVerifyInfo: Option; +} + +export const EvmVerifyRequestInfo = ({ + contractAddress, + evmVerifyInfo, +}: EvmVerifyRequestInfoProps) => { + const isMobile = useMobile(); + + // Verification not submitted + if (!evmVerifyInfo) + return ( + <> + + Contract Address: + + + + + No verification was submitted. + + + ); + + return ( + <> + + + Contract Address: + + + + License Type: + + {getLicenseTypeLabel(evmVerifyInfo.license)} + + + + + + + Language: + + {evmVerifyInfo.language} + + + + Compiler Version: + + {evmVerifyInfo.compilerVersion} + + + + Submitted on: + + {formatUTC(evmVerifyInfo.submittedTimestamp)} + + + + + ); +}; diff --git a/src/lib/components/modal/evm-verify-status/index.tsx b/src/lib/components/modal/evm-verify-status/index.tsx new file mode 100644 index 000000000..38ea89519 --- /dev/null +++ b/src/lib/components/modal/evm-verify-status/index.tsx @@ -0,0 +1,103 @@ +import { + Button, + Divider, + Flex, + Heading, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalOverlay, + useDisclosure, +} from "@chakra-ui/react"; +import type { ReactNode } from "react"; + +import { CustomIcon } from "../../icon"; +import type { HexAddr20, Option } from "lib/types"; +import { EvmVerifyInfo } from "lib/services/types"; +import { EvmVerifyRequestInfo } from "./EvmVerifyRequestInfo"; +import { EvmVerifyAlert } from "./EvmVerifyAlert"; +import { EvmVerifyProcess } from "./EvmVerifyProcess"; + +interface EvmVerifyStatusModalProps { + contractAddress: HexAddr20; + evmVerifyInfo: Option; + isOpen: boolean; + onClose: () => void; +} + +export const EvmVerifyStatusModal = ({ + contractAddress, + evmVerifyInfo, + isOpen, + onClose, +}: EvmVerifyStatusModalProps) => ( + + + + + + + + Contract Verification Status + + + + + + {evmVerifyInfo?.errorMessage && ( + + )} + + + + {evmVerifyInfo && } + + + + + +); + +interface EvmVerifyStatusModalTriggerProps + extends Pick { + triggerElement: ReactNode; +} + +export const EvmVerifyStatusModalWithTrigger = ({ + triggerElement, + contractAddress, + evmVerifyInfo, +}: EvmVerifyStatusModalTriggerProps) => { + const { isOpen, onClose, onOpen } = useDisclosure(); + + return ( + <> + { + e.stopPropagation(); + onOpen(); + }} + > + {triggerElement} + + + + ); +}; diff --git a/src/lib/components/modal/evm-verify-status/utils.ts b/src/lib/components/modal/evm-verify-status/utils.ts new file mode 100644 index 000000000..82f7322a6 --- /dev/null +++ b/src/lib/components/modal/evm-verify-status/utils.ts @@ -0,0 +1,76 @@ +import { EvmVerifyInfo } from "lib/services/types"; +import type { Option } from "lib/types"; + +enum ProcessStepState { + PENDING = "Pending", + IN_PROGRESS = "In Progress", + COMPLETED = "Completed", + FAILED = "Failed", +} + +interface ProcessStep { + label: string; + state: ProcessStepState; + timestamp?: Date; + errorMsg?: string; +} + +const getProcessStep = ( + date: Option, + errorMsg: Option, + prevStepState?: ProcessStepState +) => { + if ( + prevStepState === ProcessStepState.FAILED || + prevStepState === ProcessStepState.PENDING || + prevStepState === ProcessStepState.IN_PROGRESS + ) + return { + state: ProcessStepState.PENDING, + }; + + if (date) + return { + timestamp: date, + state: ProcessStepState.COMPLETED, + }; + + if (errorMsg) + return { + state: ProcessStepState.FAILED, + errorMsg, + }; + + return { + state: ProcessStepState.IN_PROGRESS, + }; +}; + +export const getProcessSteps = ( + evmVerifyInfo: EvmVerifyInfo +): ProcessStep[] => { + const step1 = { + label: "Submitted Source Code", + ...getProcessStep( + evmVerifyInfo.submittedTimestamp, + evmVerifyInfo.errorMessage + ), + }; + const step2 = { + label: "Verifying", + ...getProcessStep( + evmVerifyInfo.verifiedTimestamp, + evmVerifyInfo.errorMessage, + step1.state + ), + }; + const step3 = { + label: "Verification Completed", + state: + step2.state === ProcessStepState.COMPLETED + ? ProcessStepState.COMPLETED + : ProcessStepState.PENDING, + }; + + return [step1, step2, step3]; +}; diff --git a/src/lib/pages/evm-contract-details/components/EvmContractDetailsOverview.tsx b/src/lib/pages/evm-contract-details/components/EvmContractDetailsOverview.tsx index 6e484da65..1d528ed3a 100644 --- a/src/lib/pages/evm-contract-details/components/EvmContractDetailsOverview.tsx +++ b/src/lib/pages/evm-contract-details/components/EvmContractDetailsOverview.tsx @@ -3,7 +3,7 @@ import { Flex, Grid, Spinner, Stack, Text } from "@chakra-ui/react"; import type { TxsTabIndex } from "../types"; import { useCelatoneApp, useMobile } from "lib/app-provider"; import { AssetsSection } from "lib/components/asset"; -import { NotVerifiedDetails } from "lib/components/evm-verify-section"; +import { EvmVerifySection } from "lib/components/evm-verify-section"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { LabelText } from "lib/components/LabelText"; import { useFormatAddresses } from "lib/hooks/useFormatAddresses"; @@ -17,6 +17,7 @@ import type { import { dateFromNow, formatEvmTxHash, formatUTC } from "lib/utils"; import { EvmContractDetailsTxs } from "./EvmContractDetailsTxs"; +import { useEvmVerifyInfo } from "lib/services/verification/evm"; interface EvmContractDetailsOverviewProps { contractAddressBech: BechAddr20; @@ -48,11 +49,14 @@ export const EvmContractDetailsOverview = ({ const { currentChainId } = useCelatoneApp(); const formatAddresses = useFormatAddresses(); const isMobile = useMobile(); + const { data: evmVerifyInfo } = useEvmVerifyInfo(contractAddressHex); return ( - {/* // TODO: Support all status */} - + ; } -export const EvmContractDetailsContract = ({ +export const EvmContractDetailsContractInfo = ({ contractAddress, byteCode, deployedByteCode, evmVerifyInfo, -}: EvmContractDetailsContractProps) => { +}: EvmContractDetailsContractInfoProps) => { + const isVerified = evmVerifyInfo?.isVerified; const [currentTab, setCurrentTab] = useState( - evmVerifyInfo - ? EvmContractDetailsContractTabs.Code - : EvmContractDetailsContractTabs.ByteCode + isVerified + ? EvmContractDetailsContractInfoTabs.Code + : EvmContractDetailsContractInfoTabs.ByteCode ); return ( - {/* // TODO: Support all status */} - - {evmVerifyInfo && ( + + {isVerified ? ( <> - {currentTab === EvmContractDetailsContractTabs.Code && ( + {currentTab === EvmContractDetailsContractInfoTabs.Code && ( )} - {currentTab === EvmContractDetailsContractTabs.Compiler && ( + {currentTab === EvmContractDetailsContractInfoTabs.Compiler && ( )} - {currentTab === EvmContractDetailsContractTabs.Abi && ( - + {currentTab === EvmContractDetailsContractInfoTabs.Abi && ( + + )} + {currentTab === EvmContractDetailsContractInfoTabs.ByteCode && ( + )} - )} - {currentTab === EvmContractDetailsContractTabs.ByteCode && ( + ) : ( Contract + - {/* TODO: move to a proper location */} - - + + + diff --git a/src/lib/pages/evm-contract-details/types.ts b/src/lib/pages/evm-contract-details/types.ts index 36435bc75..f0c18c0e5 100644 --- a/src/lib/pages/evm-contract-details/types.ts +++ b/src/lib/pages/evm-contract-details/types.ts @@ -5,6 +5,7 @@ import { zHexAddr20 } from "lib/types"; export enum TabIndex { Overview = "overview", Contract = "contract", + ReadWrite = "read-write", Assets = "assets", Transactions = "transactions", } @@ -16,7 +17,7 @@ export enum InteractTabsIndex { WriteProxy = "write-proxy", } -export enum EvmContractDetailsContractTabs { +export enum EvmContractDetailsContractInfoTabs { Code = "Code", Compiler = "Compiler", Abi = "ABI", diff --git a/src/lib/pages/evm-contract-verify/components/EvmContractVerifyFooter.tsx b/src/lib/pages/evm-contract-verify/components/EvmContractVerifyFooter.tsx index 3b689048d..606a926b2 100644 --- a/src/lib/pages/evm-contract-verify/components/EvmContractVerifyFooter.tsx +++ b/src/lib/pages/evm-contract-verify/components/EvmContractVerifyFooter.tsx @@ -1,11 +1,8 @@ -import { CustomIcon } from "lib/components/icon"; import { FooterCta } from "lib/components/layouts"; interface EvmContractFooterProps { handleNext: () => void; handlePrevious: () => void; - hasNext: boolean; - hasPrevious: boolean; isDisabled: boolean; actionLabel: string; } @@ -13,8 +10,6 @@ interface EvmContractFooterProps { export const EvmContractFooter = ({ handleNext, handlePrevious, - hasNext, - hasPrevious, isDisabled, actionLabel, }: EvmContractFooterProps) => ( @@ -22,17 +17,11 @@ export const EvmContractFooter = ({ cancelButton={{ onClick: handlePrevious, variant: "outline-primary", - leftIcon: hasPrevious ? ( - - ) : undefined, }} - cancelLabel={hasPrevious ? "Previous" : "Cancel"} + cancelLabel="Cancel" actionButton={{ onClick: handleNext, isDisabled, - rightIcon: hasNext ? ( - - ) : undefined, }} actionLabel={actionLabel} sx={{ diff --git a/src/lib/pages/evm-contract-verify/helpers.ts b/src/lib/pages/evm-contract-verify/helpers.ts index e9a078833..78793c1f5 100644 --- a/src/lib/pages/evm-contract-verify/helpers.ts +++ b/src/lib/pages/evm-contract-verify/helpers.ts @@ -1,10 +1,7 @@ import { HexAddr20, Option, zHexAddr20 } from "lib/types"; import { EvmContractVerifyForm } from "./types"; -import { - EvmProgrammingLanguage, - EvmVerifyConfig, - EVMVerifyLicenseType, -} from "lib/services/types"; +import { EvmProgrammingLanguage, EvmVerifyConfig } from "lib/services/types"; +import { getLicenseTypeLabel } from "lib/utils"; const CONSTRUCTOR_ARGS_DEFAULT_VALUE = { enabled: false, @@ -70,41 +67,6 @@ export const getEvmContractVerifyFormDefaultValue = ( }, }); -const getLicenseTypeLabel = (license: EVMVerifyLicenseType) => { - switch (license) { - case EVMVerifyLicenseType.None: - return "1. No License (None)"; - case EVMVerifyLicenseType.Unlicense: - return "2. The Unlicense (Unlicense)"; - case EVMVerifyLicenseType.MIT: - return "3. MIT License (MIT)"; - case EVMVerifyLicenseType.GNUGPLv2: - return "4. GNU General Public License v2.0 (GNU GPLv2)"; - case EVMVerifyLicenseType.GNUGPLv3: - return "5. GNU General Public License v3.0 (GNU GPLv3)"; - case EVMVerifyLicenseType.GNULGPLv2_1: - return "6. GNU Lesser General Public License v2.1 (GNU LGPLv2.1)"; - case EVMVerifyLicenseType.GNULGPLv3: - return "7. GNU Lesser General Public License v3.0 (GNU LGPLv3)"; - case EVMVerifyLicenseType.BSD2Clause: - return `8. BSD 2-Clause "Simplified" license (BSD 2-Clause)`; - case EVMVerifyLicenseType.BSD3Clause: - return `9. BSD 3-Clause "New" or "Revised" license (BSD 3-Clause)`; - case EVMVerifyLicenseType.MPL2_0: - return "10. Mozilla Public License 2.0 (MPL 2.0)"; - case EVMVerifyLicenseType.OSL3_0: - return "11. Open Software License 3.0 (OSL 3.0)"; - case EVMVerifyLicenseType.Apache2_0: - return "12. Apache 2.0 (Apache 2.0)"; - case EVMVerifyLicenseType.GNUAGPLv3: - return "13. GNU Affero General Public License (GNU AGPLv3)"; - case EVMVerifyLicenseType.BSL1_1: - return "14. Business Source License (BSL 1.1)"; - default: - return ""; - } -}; - export const formatEvmOptions = (values: string[]) => values.map((value) => ({ label: value, @@ -112,8 +74,8 @@ export const formatEvmOptions = (values: string[]) => })); export const getLicenseTypeOptions = (evmVerifyConfig: EvmVerifyConfig) => - evmVerifyConfig.licenseType.map((license) => ({ - label: getLicenseTypeLabel(license), + evmVerifyConfig.licenseType.map((license, index) => ({ + label: `${index + 1}. ${getLicenseTypeLabel(license)}`, value: license, })); diff --git a/src/lib/pages/evm-contract-verify/index.tsx b/src/lib/pages/evm-contract-verify/index.tsx index 338f285ae..275d2e62e 100644 --- a/src/lib/pages/evm-contract-verify/index.tsx +++ b/src/lib/pages/evm-contract-verify/index.tsx @@ -12,10 +12,10 @@ import { Heading, Stack, Text, + useDisclosure, } from "@chakra-ui/react"; import { EvmContractVerifyTop } from "./components/EvmContractVerifyTop"; import { ContractLicenseInfoAccordion } from "./components/ContractLicenseInfoAccordion"; -import { useStepper } from "lib/hooks"; import { EvmContractFooter } from "./components/EvmContractVerifyFooter"; import { ControllerInput, SelectInput } from "lib/components/forms"; import { useForm } from "react-hook-form"; @@ -40,9 +40,13 @@ import { HarthatInfoAccordion } from "./components/HardhatInfoAccordion"; import { FoundryInfoAccordion } from "./components/FoundryInfoAccordion"; import { ErrorFetching, InvalidState } from "lib/components/state"; import { HexAddr20, Option } from "lib/types"; -import { useEvmVerifyConfig } from "lib/services/verification/evm"; +import { + useEvmVerifyConfig, + useEvmVerifyInfo, +} from "lib/services/verification/evm"; import { Loading } from "lib/components/Loading"; import { EvmProgrammingLanguage, EvmVerifyConfig } from "lib/services/types"; +import { EvmVerifyStatusModal } from "lib/components/modal/evm-verify-status"; interface EvmContractVerifyBodyProps { contractAddress: Option; @@ -79,20 +83,6 @@ export const EvmContractVerifyBody = ({ const { licenseType, contractAddress, language, compilerVersion, option } = watch(); - const { handleNext, handlePrevious, hasNext, hasPrevious } = useStepper( - 1, - () => { - if ( - option === VerifyOptions.SolidityHardhat || - option === VerifyOptions.SolidityFoundry - ) { - alert("Open Verification Status Modal"); - } else { - alert("Submit!"); - } - } - ); - const { licenseTypeOptions, compilerVersionOptions } = useMemo( () => ({ licenseTypeOptions: getLicenseTypeOptions(evmVerifyConfig), @@ -104,8 +94,21 @@ export const EvmContractVerifyBody = ({ [evmVerifyConfig, language] ); + const { data: evmVerifyInfo } = useEvmVerifyInfo(contractAddress); + const { isOpen, onOpen, onClose } = useDisclosure(); + + const handleSubmit = () => { + if ( + option === VerifyOptions.SolidityHardhat || + option === VerifyOptions.SolidityFoundry + ) { + onOpen(); + } else { + alert("Submit!"); + } + }; + const isFormDisabled = () => { - // TODO: Update the validation return false; }; @@ -262,12 +265,16 @@ export const EvmContractVerifyBody = ({ ? "View Verification Status" : "Verify & Publish Contract" } - handleNext={handleNext} - handlePrevious={handlePrevious} - hasNext={hasNext} - hasPrevious={hasPrevious} + handleNext={handleSubmit} + handlePrevious={router.back} isDisabled={isFormDisabled()} /> + )} diff --git a/src/lib/services/types/verification/evm.ts b/src/lib/services/types/verification/evm.ts index f9da5a816..c58511b06 100644 --- a/src/lib/services/types/verification/evm.ts +++ b/src/lib/services/types/verification/evm.ts @@ -8,7 +8,7 @@ export enum EvmProgrammingLanguage { Vyper = "Vyper", } -export enum EVMVerifyLicenseType { +export enum EvmVerifyLicenseType { None = "none", Unlicense = "unlicense", MIT = "mit", @@ -27,7 +27,7 @@ export enum EVMVerifyLicenseType { export const zEvmVerifyConfig = z .object({ - license_type: z.array(z.nativeEnum(EVMVerifyLicenseType)), + license_type: z.array(z.nativeEnum(EvmVerifyLicenseType)), solidity_compiler_versions: z.array(z.string()), solidity_evm_versions: z.array(z.string()), vyper_compiler_versions: z.array(z.string()), @@ -50,26 +50,28 @@ export type EvmVerifyInfoSourceFile = z.infer; export const zEvmVerifyInfo = z .object({ guid: z.string().uuid(), - license: z.nativeEnum(EVMVerifyLicenseType), + license: z.nativeEnum(EvmVerifyLicenseType), language: z.nativeEnum(EvmProgrammingLanguage), chain_id: z.string(), address: zHexAddr20, - contract_name: z.string(), - contract_path: z.string(), + contract_name: z.string().optional(), + contract_path: z.string().optional(), compiler_version: z.string(), evm_version: z.string(), optimizer: z.string(), constructor_arguments: z.string(), - abi: z.string(), + abi: z.string().optional(), bytecode_type: z.string(), - verified_timestamp: zUtcDate, + verified_timestamp: zUtcDate.optional(), submitted_timestamp: zUtcDate, + error_message: z.string().optional(), // libraries: z.array() // TODO: Recheck settings: z.string(), source_files: z.array(zEvmVerifyInfoSourceFile), }) .transform(({ abi, ...rest }) => ({ - abi: JSON.parse(abi) as JsonFragment[], + abi: abi ? (JSON.parse(abi) as JsonFragment[]) : undefined, ...snakeToCamel(rest), + isVerified: !!rest.verified_timestamp, })); export type EvmVerifyInfo = z.infer; diff --git a/src/lib/utils/verification/evm.ts b/src/lib/utils/verification/evm.ts new file mode 100644 index 000000000..a0984cd17 --- /dev/null +++ b/src/lib/utils/verification/evm.ts @@ -0,0 +1,36 @@ +import { EvmVerifyLicenseType } from "lib/services/types"; + +export const getLicenseTypeLabel = (license: EvmVerifyLicenseType) => { + switch (license) { + case EvmVerifyLicenseType.None: + return "No License (None)"; + case EvmVerifyLicenseType.Unlicense: + return "The Unlicense (Unlicense)"; + case EvmVerifyLicenseType.MIT: + return "MIT License (MIT)"; + case EvmVerifyLicenseType.GNUGPLv2: + return "GNU General Public License v2.0 (GNU GPLv2)"; + case EvmVerifyLicenseType.GNUGPLv3: + return "GNU General Public License v3.0 (GNU GPLv3)"; + case EvmVerifyLicenseType.GNULGPLv2_1: + return "GNU Lesser General Public License v2.1 (GNU LGPLv2.1)"; + case EvmVerifyLicenseType.GNULGPLv3: + return "GNU Lesser General Public License v3.0 (GNU LGPLv3)"; + case EvmVerifyLicenseType.BSD2Clause: + return `BSD 2-Clause "Simplified" license (BSD 2-Clause)`; + case EvmVerifyLicenseType.BSD3Clause: + return `BSD 3-Clause "New" or "Revised" license (BSD 3-Clause)`; + case EvmVerifyLicenseType.MPL2_0: + return "Mozilla Public License 2.0 (MPL 2.0)"; + case EvmVerifyLicenseType.OSL3_0: + return "Open Software License 3.0 (OSL 3.0)"; + case EvmVerifyLicenseType.Apache2_0: + return "Apache 2.0 (Apache 2.0)"; + case EvmVerifyLicenseType.GNUAGPLv3: + return "GNU Affero General Public License (GNU AGPLv3)"; + case EvmVerifyLicenseType.BSL1_1: + return "Business Source License (BSL 1.1)"; + default: + return ""; + } +}; diff --git a/src/lib/utils/verification/index.ts b/src/lib/utils/verification/index.ts index e4638e151..7e01ce292 100644 --- a/src/lib/utils/verification/index.ts +++ b/src/lib/utils/verification/index.ts @@ -1 +1,2 @@ export * from "./wasm"; +export * from "./evm";