Make receipts view prettier

pull/5285/head
Manuel Wedler 2 months ago committed by Aniket
parent 668c744c72
commit 722c9c5a81
  1. 1
      apps/contract-verification/src/app/App.css
  2. 4
      apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts
  3. 154
      apps/contract-verification/src/app/components/AccordionReceipt.tsx
  4. 2
      apps/contract-verification/src/app/types/VerificationTypes.ts
  5. 6
      apps/contract-verification/src/app/views/ReceiptsView.tsx

@ -3,3 +3,4 @@ body {
}
.fa-arrow-up-right-from-square::before { content: "\f08e"; }
.fa-xmark::before { content: "\f00d"; }

@ -75,6 +75,10 @@ export class EtherscanVerifier extends AbstractVerifier {
const verificationResponse: EtherscanRpcResponse = await response.json()
if (verificationResponse.result.includes('already verified')) {
return { status: 'already verified', receiptId: null, lookupUrl: this.getContractCodeUrl(submittedContract.address) }
}
if (verificationResponse.status !== '1' || verificationResponse.message !== 'OK') {
console.error('Error on Etherscan API verification at ' + this.apiUrl + '\nStatus: ' + verificationResponse.status + '\nMessage: ' + verificationResponse.message + '\nResult: ' + verificationResponse.result)
throw new Error(verificationResponse.result)

@ -2,6 +2,7 @@ import React, { useMemo } from 'react'
import { SubmittedContract, VerificationReceipt } from '../types'
import { shortenAddress, CustomTooltip } from '@remix-ui/helper'
import { AppContext } from '../AppContext'
import { CopyToClipboard } from '@remix-ui/clipboard'
interface AccordionReceiptProps {
contract: SubmittedContract
@ -13,67 +14,71 @@ export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, in
const [expanded, setExpanded] = React.useState(false)
const chainName = useMemo(() => {
return chains.find((chain) => chain.chainId === parseInt(contract.chainId))?.name ?? 'Unknown Chain'
const chain = useMemo(() => {
return chains.find((c) => c.chainId === parseInt(contract.chainId))
}, [contract, chains])
const chainName = chain?.name ?? 'Unknown Chain'
const hasProxy = contract.proxyAddress && contract.proxyReceipts
const toggleAccordion = () => {
setExpanded(!expanded)
}
return (
<div key={contract.address + '-' + index} className="bg-secondary p-3 accordion-item" id={contract.address + '-accordion-' + index}>
<h3 className="accordion-header" id={`heading${index}`}>
<button className="accordion-button d-flex flex-row align-items-center text-left w-100 border-0" type="button" onClick={toggleAccordion} aria-expanded={expanded} aria-controls={`collapse${index}`}>
<span className={`accordion-arrow ${expanded ? 'fa-angle-down' : 'fa-angle-right'} fa w-0`} style={{ width: '0' }}></span>
<span className="pl-4" style={{ fontSize: '1rem' }}>
<CustomTooltip tooltipText={contract.address}>
<span>{shortenAddress(contract.address)}</span>
</CustomTooltip>{' '}
on {chainName} {contract.proxyAddress ? 'with proxy' : ''}
</span>
<div className={`${expanded ? 'bg-light' : 'border-bottom '}`}>
<div className="d-flex flex-row align-items-center">
<button className="btn" onClick={toggleAccordion} style={{ padding: '0.45rem' }}>
<i className={`fas ${expanded ? 'fa-angle-down' : 'fa-angle-right'} text-secondary`}></i>
</button>
</h3>
<div id={`collapse${index}`} className={`accordion-collapse p-2 collapse ${expanded ? 'show' : ''}`} aria-labelledby={`heading${index}`} data-bs-parent="#receiptsAccordion">
<div className="accordion-body">
<div>
<span className="font-weight-bold">Chain ID: </span>
{contract.chainId}
</div>
<div>
<span className="font-weight-bold">File: </span>
<span style={{ wordBreak: 'break-word' }}>{contract.filePath}</span>
</div>
<div>
<span className="font-weight-bold">Contract: </span>
<span style={{ wordBreak: 'break-word' }}>{contract.contractName}</span>
</div>
<div>
<span className="font-weight-bold">Submission: </span>
{new Date(contract.date).toLocaleString()}
</div>
{!contract.proxyAddress ? (
<ReceiptsBody receipts={contract.receipts}></ReceiptsBody>
) : (
<>
<div>
<span className="font-weight-bold" style={{ fontSize: '1.2rem' }}>
Implementation
</span>
<ReceiptsBody receipts={contract.receipts}></ReceiptsBody>
</div>
<div className="mt-3">
<span className="font-weight-bold" style={{ fontSize: '1.2rem' }}>
Proxy
</span>{' '}
<CustomTooltip tooltipText={contract.proxyAddress}>
<span>{shortenAddress(contract.proxyAddress)}</span>
</CustomTooltip>
<ReceiptsBody receipts={contract.proxyReceipts}></ReceiptsBody>
</div>
</>
)}
<div className="small w-100 text-uppercase overflow-hidden text-break text-nowrap">
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipText={`Contract: ${contract.contractName}, Address: ${contract.address}, Chain: ${chainName}, Proxy: ${contract.proxyAddress}`}>
<span>
{contract.contractName} at {shortenAddress(contract.address)} {contract.proxyAddress ? 'with proxy' : ''}
</span>
</CustomTooltip>
</div>
<button className="btn" style={{ padding: '0.15rem' }}>
<CopyToClipboard tip="Copy" content={contract.address} direction={'top'} />
</button>
</div>
<div className={`${expanded ? '' : 'd-none'} px-2 pt-2 pb-3 small`}>
<div>
<span className="font-weight-bold">Chain: </span>
{chainName} ({contract.chainId})
</div>
<div>
<span className="font-weight-bold">File: </span>
<span className="text-break">{contract.filePath}</span>
</div>
<div>
<span className="font-weight-bold">Submitted at: </span>
{new Date(contract.date).toLocaleString()}
</div>
<div>
<span className="font-weight-bold">Verified at: </span>
<ReceiptsBody receipts={contract.receipts} />
</div>
{hasProxy && (
<>
<div className="mt-3">
<span className="font-weight-bold">Proxy Address: </span>
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipText={contract.proxyAddress}>
<span>{shortenAddress(contract.proxyAddress)}</span>
</CustomTooltip>
<CopyToClipboard tip="Copy" content={contract.proxyAddress} direction={'top'} />
</div>
<div>
<span className="font-weight-bold">Proxy verified at: </span>
<ReceiptsBody receipts={contract.proxyReceipts} />
</div>
</>
)}
</div>
</div>
)
@ -81,35 +86,20 @@ export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, in
const ReceiptsBody = ({ receipts }: { receipts: VerificationReceipt[] }) => {
return (
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>Verifier</th>
<th>API URL</th>
<th>Status</th>
<th>Message</th>
<th>Link</th>
<th>ReceiptID</th>
</tr>
</thead>
<tbody>
{receipts.map((receipt) => (
<tr key={`${receipt.isProxyReceipt ? 'proxy' : ''}-${receipt.receiptId}-${receipt.verifierInfo.name}`}>
<td>{receipt.verifierInfo.name}</td>
<td>{receipt.verifierInfo.apiUrl}</td>
<td>
<span className="font-weight-bold" style={{ textTransform: 'capitalize' }}>
{receipt.status}
</span>
</td>
<td>{receipt.message}</td>
<td>{!!receipt.lookupUrl && <a href={receipt.lookupUrl} target="_blank" className="fa fas fa-arrow-up-right-from-square"></a>}</td>
<td>{receipt.receiptId}</td>
</tr>
))}
</tbody>
</table>
</div>
<ul className="list-group">
{receipts.map((receipt) => (
<li className="list-group-item">
<CustomTooltip placement="top" tooltipText={`API: ${receipt.verifierInfo.apiUrl}`}>
<span className="font-weight-bold medium">{receipt.verifierInfo.name}</span>
</CustomTooltip>
<CustomTooltip placement="top" tooltipTextClasses="text-capitalize" tooltipText={`Status: ${receipt.status}${receipt.message ? `, Message: ${receipt.message}` : ''}`}>
<span className="ml-2">{['verified', 'partially verified', 'already verified'].includes(receipt.status) ? <i className="fas fa-check"></i> : receipt.status === 'fully verified' ? <i className="fas fa-check-double"></i> : receipt.status === 'failed' ? <i className="fas fa-xmark"></i> : ['pending', 'awaiting implementation verification'].includes(receipt.status) ? <i className="fas fa-spinner fa-spin"></i> : <i className="fas fa-question"></i>}</span>
</CustomTooltip>
<span className="ml-2">{!!receipt.lookupUrl && <a href={receipt.lookupUrl} target="_blank" className="fa fas fa-arrow-up-right-from-square"></a>}</span>
</li>
))}
</ul>
)
}

@ -57,7 +57,7 @@ export interface SubmittedContracts {
type SourcifyStatus = 'fully verified' | 'partially verified'
type EtherscanStatus = 'verified' | 'already verified'
export type VerificationStatus = SourcifyStatus | EtherscanStatus | 'failed' | 'pending' | 'not verified' | 'unknown' | 'lookup failed' | 'awaiting implementation verification'
export type VerificationStatus = SourcifyStatus | EtherscanStatus | 'failed' | 'pending' | 'awaiting implementation verification' | 'not verified' | 'lookup failed' | 'unknown'
export interface VerificationResponse {
status: VerificationStatus

@ -7,8 +7,10 @@ export const ReceiptsView = () => {
const contracts = Object.values(submittedContracts).reverse()
return (
<div className="accordion" id="receiptsAccordion">
{contracts.length > 0 ? contracts.map((contract, index) => <AccordionReceipt contract={contract} index={index} />) : <div className="text-center mt-5">No contracts submitted for verification</div>}
<div>
{contracts.length > 0 ? contracts.map((contract, index) => (
<AccordionReceipt contract={contract} index={index} />
)) : <div className="text-center mt-5">No contracts submitted for verification</div>}
</div>
)
}

Loading…
Cancel
Save