Skip to content

Commit

Permalink
Merge pull request #117 from AstraProtocol/fix/verify-contract-endpoint
Browse files Browse the repository at this point in the history
fix: verify contract endpoint + UX
  • Loading branch information
viennguyen2-tiki authored Apr 3, 2023
2 parents 7f12e26 + a7eda38 commit dd23e01
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 25 deletions.
4 changes: 2 additions & 2 deletions api/api_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ const API_LIST = {
TOKEN_TRANSER_BY_TOKEN_ID: '/api/v1/contract/token-transfers-by-tokenid/', // contractaddress=0x8CB41dA24793D4515E6b96D1adA50b721878C0Ca&tokenid=10

CONTRACT_CODE: '/api/v1/contract/source-code/',
VERIFY_CONTRACT: `evm_/verify_smart_contract/contract_verifications`,
CHECK_VERIFY_STATUS: 'evm_/api/v1?module=contract&action=checkverifystatus',
VERIFY_CONTRACT: `/verify_smart_contract/contract_verifications`,
CHECK_VERIFY_STATUS: '/api?module=contract&action=checkverifystatus',
GET_EVM_VERSION: '/api/v1/evm-versions',
GET_SOLIDITY_COMPILER: '/api/v1/compiler-versions/',

Expand Down
13 changes: 8 additions & 5 deletions components/FormItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface InputNumberData extends Tooltip {}
export interface TextAreaData extends Tooltip {}

interface Props {
disabled?: boolean
label: string
type: 'input' | 'select' | 'radio-button' | 'text-field' | 'input-number' | 'file'
inputProps: {
Expand All @@ -42,15 +43,15 @@ interface Props {
}
}

const FormItem = ({ label, type, inputProps }: Props) => {
const FormItem = ({ label, type, inputProps, disabled = false }: Props) => {
let content
let data
let props
switch (type) {
case 'input':
props = inputProps.props as InputProps
data = inputProps.data as InputData
content = <Form.Input data-tip={data?.tooltip} data-for={data?.id} {...props} />
content = <Form.Input disabled={disabled} data-tip={data?.tooltip} data-for={data?.id} {...props} />
break
case 'radio-button':
props = inputProps.props as RadioButtonProps
Expand All @@ -60,6 +61,7 @@ const FormItem = ({ label, type, inputProps }: Props) => {
<Row>
{data.items.map((o: Option) => (
<RadioButton
disabled={disabled}
{...props}
text={o.label}
value={o.value}
Expand All @@ -79,6 +81,7 @@ const FormItem = ({ label, type, inputProps }: Props) => {

content = (
<Form.Select
isDisabled={disabled}
data-tip={data?.tooltip}
data-for={data?.id}
onChange={value => props.onSelect(value)}
Expand All @@ -91,16 +94,16 @@ const FormItem = ({ label, type, inputProps }: Props) => {
case 'input-number':
props = inputProps.props as InputNumberProps
data = inputProps.data as InputNumberData
content = <Form.NumberInput data-tip={data?.tooltip} data-for={data?.id} {...props} />
content = <Form.NumberInput disabled={disabled} data-tip={data?.tooltip} data-for={data?.id} {...props} />
break
case 'text-field':
props = inputProps.props as TextAreaProps
data = inputProps.data as TextAreaData
content = <Form.TextArea data-tip={data?.tooltip} data-for={data?.id} {...props} />
content = <Form.TextArea disabled={disabled} data-tip={data?.tooltip} data-for={data?.id} {...props} />
break
case 'file':
content = (
<Dropzone onDrop={acceptedFiles => console.log(acceptedFiles)}>
<Dropzone disabled={disabled} onDrop={acceptedFiles => console.log(acceptedFiles)}>
{({ getRootProps, getInputProps }) => (
<section className="container" style={{ borderWidth: 1, borderColor: 'red' }}>
<div {...getRootProps({ className: 'dropzone' })}>
Expand Down
3 changes: 2 additions & 1 deletion components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Footer } from '@astraprotocol/astra-ui'
import { Footer, ToastWrapper } from '@astraprotocol/astra-ui'
import API_LIST from 'api/api_list'
import clsx from 'clsx'
import React, { ReactNode, useEffect } from 'react'
Expand Down Expand Up @@ -55,6 +55,7 @@ const Layout: React.FC<Props> = props => {
<div className={styles.layout}>{props.children}</div>
<Footer />
<div id="modal-root"></div>
<ToastWrapper />
</div>
)
}
Expand Down
45 changes: 35 additions & 10 deletions components/VerifyContract/ContractFlattenedVerify.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NormalButton } from '@astraprotocol/astra-ui'
import { NormalButton, withToast } from '@astraprotocol/astra-ui'
import { InputProps } from '@astraprotocol/astra-ui/lib/es/components/Form/Input'
import { InputProps as InputNumberProps } from '@astraprotocol/astra-ui/lib/es/components/Form/Input/NumberInput'
import * as Sentry from '@sentry/react'
Expand All @@ -11,7 +11,7 @@ import qs from 'qs'
import { useEffect, useRef, useState } from 'react'
import AddressDisplay from './AddressDisplay'
import Header from './Header'
import useContractVerifyStatus from './hook/useContractVerifyStatus'
import useContractVerifyStatus, { Status } from './hook/useContractVerifyStatus'
import useEvmVersion from './hook/useEvmVersion'
import useSolidityCompiler from './hook/useSolidityCompiler'
import styles from './style.module.scss'
Expand All @@ -27,7 +27,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
const [contractName, setContractName] = useState('')
const [hasNightlyBuild, setHasNightlyBuild] = useState(true)
const [compiler, setCompiler] = useState(undefined)
const [evmVersion, setEvmVersion] = useState('default')
const [evmVersion, setEvmVersion] = useState(undefined)
const [hasOptimization, setOptimization] = useState(true)
const [optimizeRun, setOptimizeRun] = useState(200)
const [solidityCode, setSolidityCode] = useState('')
Expand All @@ -38,7 +38,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {

const versions = useEvmVersion()
const solidityCompilers = useSolidityCompiler()
const isValidated = useContractVerifyStatus(guid)
const [status, errorMessage] = useContractVerifyStatus(guid)

const addLibraryItem = () => {
if (libs.length > 10) return
Expand Down Expand Up @@ -85,7 +85,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
const data = qs.stringify(params)
var config = {
method: 'post',
url: API_LIST.VERIFY_CONTRACT,
url: `${process.env.NEXT_PUBLIC_COSMOS_API}${API_LIST.VERIFY_CONTRACT}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
Expand All @@ -106,8 +106,23 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}

useEffect(() => {
if (isValidated) onSuccess()
}, [isValidated])
if (status !== Status.Waiting) {
setGuid(undefined)
setLoading(false)

const isSuccess = status === Status.Validated
if (isSuccess) onSuccess()
else {
withToast(
{
title: 'Error',
moreInfo: errorMessage
},
{ type: 'error' }
)
}
}
}, [status])

return (
<div className={clsx(styles.modalVerify, 'radius-lg')}>
Expand All @@ -116,6 +131,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
<AddressDisplay classes="margin-bottom-xl" address={address} />
<div className={clsx('margin-bottom-xl border border-bottom-base', styles.borderColor)}>
<FormItem
disabled={loading}
label="Contract Name:"
type="input"
inputProps={{
Expand All @@ -139,6 +155,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
label="Include nightly build:"
type="radio-button"
inputProps={{
Expand All @@ -158,6 +175,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
label="Compiler:"
type="select"
inputProps={{
Expand All @@ -174,6 +192,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
label="EVM Version:"
type="select"
inputProps={{
Expand All @@ -191,6 +210,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
label="Optimization:"
type="radio-button"
inputProps={{
Expand All @@ -211,6 +231,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
/>

<FormItem
disabled={loading}
label="Optimize run:"
type="input-number"
inputProps={{
Expand All @@ -223,6 +244,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
label="Enter the Solidity Contract Code:"
type="text-field"
inputProps={{
Expand All @@ -239,6 +261,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
label="Try to fetch constructor arguments automatically:"
type="radio-button"
inputProps={{
Expand All @@ -261,6 +284,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
return (
<>
<FormItem
disabled={loading}
key={lib.index}
label={`Library ${lib.index + 1} Name:`}
type="input"
Expand All @@ -286,6 +310,7 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
}}
/>
<FormItem
disabled={loading}
key={lib.index}
label={`Library ${lib.index + 1} Address:`}
type="input"
Expand Down Expand Up @@ -313,22 +338,22 @@ const ContractFlattenedVerify = ({ address, onClose, onSuccess }: Props) => {
</>
)
})}
<NormalButton style={{ width: 207, marginRight: 10 }} onClick={addLibraryItem}>
<NormalButton disabled={loading} style={{ width: 207, marginRight: 10 }} onClick={addLibraryItem}>
<span className="text text-base contrast-color-100">Add Contract Library</span>
</NormalButton>
</div>
<Row
style={{ justifyContent: 'space-between' }}
classes={clsx(styles.paddingHoz, styles.footer, 'padding-top-md padding-bottom-md')}
>
<NormalButton style={{ width: 78 }} onClick={onReset} variant="default">
<NormalButton disabled={loading} style={{ width: 78 }} onClick={onReset} variant="default">
<span className="text text-base contrast-color-100">Reset</span>
</NormalButton>
<Row style={{ flex: 0 }}>
<NormalButton loading={loading} style={{ width: 170, marginRight: 10 }} onClick={onVerify}>
<span className="text text-base contrast-color-100">Verify and Publish</span>
</NormalButton>
<NormalButton style={{ width: 86 }} onClick={() => onClose()} variant="text">
<NormalButton disabled={loading} style={{ width: 86 }} onClick={() => onClose()} variant="text">
<span className="text text-base contrast-color-100">Cancel</span>
</NormalButton>
</Row>
Expand Down
29 changes: 24 additions & 5 deletions components/VerifyContract/hook/useContractVerifyStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ import API_LIST from 'api/api_list'
import { useEffect, useState } from 'react'
import useSWR from 'swr'

const useContractVerifyStatus = (guid: string): boolean => {
const [validated, setValidate] = useState(false)
export enum Status {
Waiting,
Validated,
Failed
}

const useContractVerifyStatus = (guid: string): [Status, string | undefined] => {
const [status, setValidate] = useState(Status.Waiting)
const [errorMessage, setErrorMessage] = useState(undefined)
const _fetchCondition = () => {
if (!guid) return null
return [
Expand All @@ -14,15 +21,27 @@ const useContractVerifyStatus = (guid: string): boolean => {
]
}
const { data } = useSWR<VerifyStatusResponse>(_fetchCondition(), {
refreshInterval: 2
refreshInterval: 2000
})

useEffect(() => {
if (data && data?.result) {
if (data?.result.includes('Pass - Verified')) setValidate(true)
if (data?.result.includes('Pass')) setValidate(Status.Validated)
else if (data?.result.includes('Fail')) {
setValidate(Status.Failed)
setErrorMessage(data?.result)
}
}
}, [data])
return validated

useEffect(() => {
if (!guid) {
setValidate(Status.Waiting)
setErrorMessage(undefined)
}
}, [guid])

return [status, errorMessage]
}

export default useContractVerifyStatus
3 changes: 1 addition & 2 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { NextWebVitalsMetric } from 'next/app'
import { AppProps } from 'next/app'
import { event, GoogleAnalytics } from 'nextjs-google-analytics'
import { Provider } from 'react-redux'
import { ToastContainer } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import { PersistGate } from 'redux-persist/integration/react'

Expand Down Expand Up @@ -94,7 +93,7 @@ const App = ({ Component, pageProps }: AppProps) => {
<>
{process.env.NODE_ENV === 'production' && <GoogleAnalytics trackPageViews />}
<PageLoader />
<ToastContainer toastClassName="dark--mode" />

<Component {...pageProps} />
</>
</SWRConfig>
Expand Down

0 comments on commit dd23e01

Please sign in to comment.