Add proxy contracts to receipts view

pull/5285/head
Manuel Wedler 5 months ago committed by Aniket
parent 8ba75251e6
commit 8147e61169
  1. 138
      apps/contract-verification/src/app/components/AccordionReceipt.tsx
  2. 18
      apps/contract-verification/src/app/types/VerificationTypes.ts
  3. 17
      apps/contract-verification/src/app/views/ReceiptsView.tsx
  4. 1
      apps/contract-verification/src/app/views/VerifyView.tsx
  5. 71
      apps/contract-verification/src/app/views/example.js

@ -1,77 +1,115 @@
import React from 'react'
import { SubmittedContract } from '../types/VerificationTypes'
import React, { useMemo } from 'react'
import { SubmittedContract, SubmittedProxyContract, isContract, isProxy } from '../types/VerificationTypes'
import { shortenAddress, CustomTooltip } from '@remix-ui/helper'
import { AppContext } from '../AppContext'
interface AccordionReceiptProps {
contract: SubmittedContract
chainName: string
contract: SubmittedContract | SubmittedProxyContract
index: number
}
export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, index, chainName }) => {
export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, index }) => {
const { chains } = React.useContext(AppContext)
const [expanded, setExpanded] = React.useState(false)
const address = isProxy(contract) ? contract.implementation.address : contract.address
const chainName = useMemo(() => {
const chainId = isProxy(contract) ? contract.implementation.chainId : contract.chainId
return chains.find((chain) => chain.chainId === parseInt(chainId))?.name ?? 'Unknown Chain'
}, [contract, chains])
const toggleAccordion = () => {
setExpanded(!expanded)
}
return (
<div key={contract.address + '-' + index} className="bg-secondary p-3 accordion-item" id={contract.address + '-accordion-' + index}>
<div key={address + '-' + index} className="bg-secondary p-3 accordion-item" id={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}
<CustomTooltip tooltipText={address}>
<span>{shortenAddress(address)}</span>
</CustomTooltip>
&nbsp;on {chainName} {isProxy(contract) ? 'with proxy' : ''}
</span>
</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>
{contract.filePath}
</div>
<div>
<span className="font-weight-bold">Contract: </span>
{contract.contractName}
</div>
<div>
<span className="font-weight-bold">Submission: </span>
{contract.date.toLocaleString()}
</div>
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>Verifier</th>
<th>API URL</th>
<th>Status</th>
<th>Message</th>
<th>ReceiptID</th>
</tr>
</thead>
<tbody>
{contract.receipts.map((receipt) => (
<tr key={receipt.receiptId}>
<td>{receipt.verifier.name}</td>
<td>{receipt.verifier.apiUrl}</td>
<td>{receipt.status}</td>
<td>{receipt.message}</td>
<td>{receipt.receiptId}</td>
</tr>
))}
</tbody>
</table>
</div>
{isContract(contract) ? (
<ReceiptsBody contract={contract}></ReceiptsBody>
) : (
<>
<div>
<span className="font-weight-bold" style={{ fontSize: '1.2rem' }}>
Implementation
</span>
<ReceiptsBody contract={contract.implementation}></ReceiptsBody>
</div>
<div className="mt-3">
<span className="font-weight-bold" style={{ fontSize: '1.2rem' }}>
Proxy
</span>
&nbsp;
<CustomTooltip tooltipText={contract.proxy.address}>
<span>{shortenAddress(contract.proxy.address)}</span>
</CustomTooltip>
<ReceiptsBody contract={contract.proxy}></ReceiptsBody>
</div>
</>
)}
</div>
</div>
</div>
)
}
const ReceiptsBody = ({ contract }: { contract: SubmittedContract }) => {
return (
<>
<div>
<span className="font-weight-bold">Chain ID: </span>
{contract.chainId}
</div>
<div>
<span className="font-weight-bold">File: </span>
{contract.filePath}
</div>
<div>
<span className="font-weight-bold">Contract: </span>
{contract.contractName}
</div>
<div>
<span className="font-weight-bold">Submission: </span>
{contract.date.toLocaleString()}
</div>
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>Verifier</th>
<th>API URL</th>
<th>Status</th>
<th>Message</th>
<th>ReceiptID</th>
</tr>
</thead>
<tbody>
{contract.receipts.map((receipt) => (
<tr key={receipt.receiptId}>
<td>{receipt.verifier.name}</td>
<td>{receipt.verifier.apiUrl}</td>
<td>{receipt.status}</td>
<td>{receipt.message}</td>
<td>{receipt.receiptId}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
)
}

@ -32,6 +32,7 @@ export interface VerificationReceipt {
}
export interface SubmittedContract {
type: 'contract'
id: string
filePath: string
contractName: string
@ -42,8 +43,23 @@ export interface SubmittedContract {
receipts: VerificationReceipt[]
}
export interface SubmittedProxyContract {
type: 'proxy'
id: string
implementation: SubmittedContract
proxy: SubmittedContract
}
export interface SubmittedContracts {
[id: string]: SubmittedContract
[id: string]: SubmittedContract | SubmittedProxyContract
}
export function isProxy(contract: SubmittedContract | SubmittedProxyContract): contract is SubmittedProxyContract {
return contract.type === 'proxy'
}
export function isContract(contract: SubmittedContract | SubmittedProxyContract): contract is SubmittedContract {
return contract.type === 'contract'
}
export interface SourcifyVerificationResponse {

@ -1,26 +1,15 @@
import React from 'react'
import { AppContext } from '../AppContext'
import example from './example.js'
import { AccordionReceipt } from '../components/AccordionReceipt'
import { SubmittedContract } from '../types/VerificationTypes'
import { SubmittedContracts } from '../types/VerificationTypes'
export const ReceiptsView = () => {
const { chains } = React.useContext(AppContext)
const submittedContracts = example as unknown as SubmittedContracts
// const {submittedContracts} = React.useContext(AppContext);
const getChainName = (chainId: string) => {
return chains.find((chain) => chain.chainId === parseInt(chainId))?.name ?? 'Unknown Chain'
}
const submittedContracts = example as unknown as Record<string,SubmittedContract>
console.log('submittedContracts', submittedContracts)
return (
<div className="accordion" id="receiptsAccordion">
{Object.values(submittedContracts).map((contract, index) => (
<AccordionReceipt contract={contract} index={index} chainName={getChainName(contract.chainId)} />
))}
{Object.values(submittedContracts).map((contract, index) => (
<AccordionReceipt contract={contract} index={index+1} chainName={getChainName(contract.chainId)} />
<AccordionReceipt contract={contract} index={index} />
))}
</div>
)

@ -51,6 +51,7 @@ export const VerifyView = () => {
// A receipt for each verifier
const receipts: VerificationReceipt[] = enabledVerifiers.map((verifier) => ({ verifier, status: null, receiptId: null, message: null }))
const newSubmittedContract: SubmittedContract = {
type: 'contract',
id: selectedChain?.chainId + '-' + contractAddress + '-' + date.toString(),
address: contractAddress,
chainId: selectedChain?.chainId.toString(),

@ -1,5 +1,6 @@
const json = {
'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)': {
type: 'contract',
id: 'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
address: '0x2738d13E81e30bC615766A0410e7cF199FD59A83',
filePath: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
@ -28,7 +29,75 @@ const json = {
message: 'Failed to fetch',
},
],
chainId: "1"
chainId: '1',
},
'1-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)': {
type: 'proxy',
id: '1-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
implementation: {
type: 'contract',
id: 'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
address: '0x2738d13E81e30bC615766A0410e7cF199FD59A83',
filePath: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
contractName: 'Initializable',
date: '2024-06-20T20:32:36.361Z',
receipts: [
{
verifier: {
name: 'Sourcify Localhost',
apiUrl: 'http://localhost:5555/',
enabled: true,
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
{
verifier: {
name: 'Etherscan',
apiUrl: 'https://api.etherscan.io',
enabled: true,
apiKey: 'API_KEY',
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
],
chainId: '1',
},
proxy: {
type: 'contract',
id: 'undefined-0x2738d13E81e30bC615766A0410e7cF199FD59A83-Thu Jun 20 2024 22:32:36 GMT+0200 (Central European Summer Time)',
address: '0x2738d13E81e30bC615766A0410e7cF199FD59A83',
filePath: '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol',
contractName: 'Initializable',
date: '2024-06-20T20:32:36.361Z',
receipts: [
{
verifier: {
name: 'Sourcify Localhost',
apiUrl: 'http://localhost:5555/',
enabled: true,
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
{
verifier: {
name: 'Etherscan',
apiUrl: 'https://api.etherscan.io',
enabled: true,
apiKey: 'API_KEY',
},
status: 'error',
receiptId: null,
message: 'Failed to fetch',
},
],
chainId: '1',
},
},
}

Loading…
Cancel
Save