11import React , { useMemo } from 'react'
2- import { SubmittedContract , VerificationReceipt } from '../types'
2+ import { SubmittedContract , VerificationReceipt , VerificationStatus } from '../types'
33import { shortenAddress , CustomTooltip } from '@remix-ui/helper'
44import { AppContext } from '../AppContext'
55import { CopyToClipboard } from '@remix-ui/clipboard'
6+ import { getVerifier } from '../Verifiers'
7+ import { CompilerAbstract } from '@remix-project/remix-solidity'
8+ import { mergeChainSettingsWithDefaults } from '../utils'
69
710interface AccordionReceiptProps {
811 contract : SubmittedContract
912 index : number
1013}
1114
1215export const AccordionReceipt : React . FC < AccordionReceiptProps > = ( { contract, index } ) => {
13- const { chains } = React . useContext ( AppContext )
16+ const { chains, settings , compilationOutput , setSubmittedContracts } = React . useContext ( AppContext )
1417
1518 const [ expanded , setExpanded ] = React . useState ( false )
1619
@@ -25,6 +28,107 @@ export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, in
2528 setExpanded ( ! expanded )
2629 }
2730
31+ const isRetryAvailable = useMemo ( ( ) => {
32+ if ( ! compilationOutput ) return false
33+
34+ const compilerAbstract = Object . values ( compilationOutput || { } ) . find (
35+ ( abstract : CompilerAbstract ) =>
36+ abstract . data . contracts [ contract . filePath ] &&
37+ abstract . data . contracts [ contract . filePath ] [ contract . contractName ]
38+ )
39+ return ! ! compilerAbstract
40+ } , [ compilationOutput , contract . filePath , contract . contractName ] )
41+
42+ const handleRetryVerification = async ( receipt : VerificationReceipt ) => {
43+ setSubmittedContracts ( prev => {
44+ const currentContract = prev [ contract . id ]
45+ if ( ! currentContract ) return prev
46+
47+ return {
48+ ...prev ,
49+ [ contract . id ] : {
50+ ...currentContract ,
51+ receipts : ( currentContract . receipts || [ ] ) . map ( r =>
52+ r === receipt ? { ...r , status : 'pending' as VerificationStatus , message : 'Retrying...' } : r
53+ ) ,
54+ proxyReceipts : ( currentContract . proxyReceipts || [ ] ) . map ( r =>
55+ r === receipt ? { ...r , status : 'pending' as VerificationStatus , message : 'Retrying...' } : r
56+ )
57+ }
58+ }
59+ } )
60+
61+ try {
62+ const chainSettings = mergeChainSettingsWithDefaults ( contract . chainId , settings )
63+ const verifierSettings = chainSettings . verifiers [ receipt . verifierInfo . name ]
64+
65+ if ( ! verifierSettings || ! verifierSettings . apiUrl ) {
66+ throw new Error ( 'Verifier settings or apiUrl not found.' )
67+ }
68+
69+ const verifier = getVerifier ( receipt . verifierInfo . name , verifierSettings )
70+
71+ let response
72+
73+ if ( receipt . isProxyReceipt ) {
74+ if ( ! verifier . verifyProxy ) {
75+ throw new Error ( `Proxy verification not supported by ${ receipt . verifierInfo . name } ` )
76+ }
77+ response = await verifier . verifyProxy ( contract )
78+ } else {
79+ const compilerAbstract = Object . values ( compilationOutput || { } ) . find (
80+ ( abstract : CompilerAbstract ) =>
81+ abstract . data . contracts [ contract . filePath ] &&
82+ abstract . data . contracts [ contract . filePath ] [ contract . contractName ]
83+ )
84+
85+ if ( ! compilerAbstract ) {
86+ const userMessage = `Compilation output not found. Please re-compile the contract file ('${ contract . filePath } ') and try again.`
87+ console . error ( `[Retry] ${ userMessage } ` )
88+ throw new Error ( userMessage )
89+ }
90+
91+ response = await verifier . verify ( contract , compilerAbstract )
92+ }
93+
94+ setSubmittedContracts ( prev => {
95+ const currentContract = prev [ contract . id ]
96+ if ( ! currentContract ) return prev
97+ return {
98+ ...prev ,
99+ [ contract . id ] : {
100+ ...currentContract ,
101+ receipts : ( currentContract . receipts || [ ] ) . map ( r =>
102+ r === receipt ? { ...r , ...response , receiptId : response . receiptId || undefined , status : response . status , message : response . message } : r
103+ ) ,
104+ proxyReceipts : ( currentContract . proxyReceipts || [ ] ) . map ( r =>
105+ r === receipt ? { ...r , ...response , receiptId : response . receiptId || undefined , status : response . status , message : response . message } : r
106+ )
107+ }
108+ }
109+ } )
110+
111+ } catch ( e ) {
112+ console . error ( e )
113+ setSubmittedContracts ( prev => {
114+ const currentContract = prev [ contract . id ]
115+ if ( ! currentContract ) return prev
116+ return {
117+ ...prev ,
118+ [ contract . id ] : {
119+ ...currentContract ,
120+ receipts : ( currentContract . receipts || [ ] ) . map ( r =>
121+ r === receipt ? { ...r , status : 'failed' as VerificationStatus , message : e . message } : r
122+ ) ,
123+ proxyReceipts : ( currentContract . proxyReceipts || [ ] ) . map ( r =>
124+ r === receipt ? { ...r , status : 'failed' as VerificationStatus , message : e . message } : r
125+ )
126+ }
127+ }
128+ } )
129+ }
130+ }
131+
28132 return (
29133 < div className = { `${ expanded ? 'bg-light' : 'border-bottom ' } ` } >
30134 < div className = "d-flex flex-row align-items-center" >
@@ -65,7 +169,7 @@ export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, in
65169
66170 < div >
67171 < span className = "fw-bold" > Verified at: </ span >
68- < ReceiptsBody receipts = { contract . receipts } />
172+ < ReceiptsBody receipts = { contract . receipts } handleRetry = { handleRetryVerification } isRetryAvailable = { isRetryAvailable } />
69173 </ div >
70174
71175 { hasProxy && (
@@ -79,7 +183,7 @@ export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, in
79183 </ div >
80184 < div >
81185 < span className = "fw-bold" > Proxy verified at: </ span >
82- < ReceiptsBody receipts = { contract . proxyReceipts } />
186+ < ReceiptsBody receipts = { contract . proxyReceipts } handleRetry = { handleRetryVerification } isRetryAvailable = { isRetryAvailable } />
83187 </ div >
84188 </ >
85189 ) }
@@ -88,13 +192,17 @@ export const AccordionReceipt: React.FC<AccordionReceiptProps> = ({ contract, in
88192 )
89193}
90194
91- const ReceiptsBody = ( { receipts } : { receipts : VerificationReceipt [ ] } ) => {
195+ const ReceiptsBody = ( { receipts, handleRetry, isRetryAvailable } : {
196+ receipts : VerificationReceipt [ ] ,
197+ handleRetry : ( receipt : VerificationReceipt ) => void ,
198+ isRetryAvailable : boolean
199+ } ) => {
92200 return (
93201 < ul className = "list-group" >
94202 { receipts . map ( ( receipt ) => (
95203 < li
96204 key = { `${ receipt . contractId } -${ receipt . verifierInfo . name } ${ receipt . isProxyReceipt ? '-proxy' : '' } -${ receipt . receiptId } ` }
97- className = "list-group-item d-flex flex-row align-items-center "
205+ className = "list-group-item d-flex flex-row align-items-baseline "
98206 >
99207 < CustomTooltip
100208 placement = "top"
@@ -109,12 +217,23 @@ const ReceiptsBody = ({ receipts }: { receipts: VerificationReceipt[] }) => {
109217 < i className = "fas fa-check-double text-success px-1" > </ i > :
110218 receipt . status === 'failed' ?
111219 < i className = "fas fa-xmark text-warning px-1" > </ i > :
112- [ 'pending' , 'awaiting implementation verification' ] . includes ( receipt . status ) ?
220+ [ 'pending' , 'awaiting implementation verification' , 'Retrying...' ] . includes ( receipt . status ) ?
113221 < i className = "fas fa-spinner fa-spin px-1" > </ i > :
114222 < i className = "fas fa-question px-1" > </ i >
115223 }
116224 </ span >
117225 </ CustomTooltip >
226+ { receipt . status === 'failed' && isRetryAvailable && (
227+ < CustomTooltip placement = "top" tooltipText = "Retry Verification" >
228+ < button
229+ className = "btn btn-sm p-0 me-2"
230+ style = { { border : 'none' , background : 'none' , color : 'var(--primary)' } }
231+ onClick = { ( ) => handleRetry ( receipt ) }
232+ >
233+ < i className = "fas fa-redo" style = { { fontSize : '0.6rem' } } > </ i >
234+ </ button >
235+ </ CustomTooltip >
236+ ) }
118237 < div className = "d-flex flex-row w-100 justify-content-between" >
119238 < div >
120239 < CustomTooltip placement = "top" tooltipClasses = " text-break" tooltipText = { `API: ${ receipt . verifierInfo . apiUrl } ` } >
0 commit comments