Skip to content

Commit d9f04de

Browse files
hsy822Aniket-Engg
authored andcommitted
verify after lookup
1 parent 36961b4 commit d9f04de

File tree

2 files changed

+70
-41
lines changed

2 files changed

+70
-41
lines changed

apps/contract-verification/src/app/ContractVerificationPluginClient.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ export class ContractVerificationPluginClient extends PluginClient {
7878
return
7979
}
8080

81-
await this.call('terminal', 'log', { type: 'info', value: `[Verification] Contract deployed. Starting verification process for ${contractName}...` });
82-
await new Promise(resolve => setTimeout(resolve, 7000));
81+
await this.call('terminal', 'log', { type: 'info', value: `[Verification] Contract deployed. Checking explorers for registration...` });
8382

8483
try {
8584
const allArtifacts = await this.call('compilerArtefacts' as any, 'getAllCompilerAbstracts')
@@ -147,7 +146,6 @@ export class ContractVerificationPluginClient extends PluginClient {
147146
await this.call('terminal', 'log', { type: 'warn', value: `Please input the API key in Remix Settings - Connected Services OR Contract Verification Plugin Settings.` })
148147
continue
149148
}
150-
151149
if (hasApiUrl && etherscanApiKeySource === 'global') {
152150
await this.call('terminal', 'log', { type: 'log', value: '[Etherscan] Using API key from Remix global settings.' })
153151
}
@@ -186,6 +184,33 @@ export class ContractVerificationPluginClient extends PluginClient {
186184
const verifierSettings = chainSettings.verifiers[verifierId]
187185
const verifier = getVerifier(verifierId, verifierSettings)
188186

187+
let isExplorerReady = false
188+
let checkAttempts = 0
189+
const maxCheckAttempts = 10
190+
191+
while (checkAttempts < maxCheckAttempts) {
192+
checkAttempts++
193+
try {
194+
await verifier.lookup(address, chainId)
195+
isExplorerReady = true
196+
break
197+
} catch (lookupError: any) {
198+
const errMsg = lookupError.message || ''
199+
if (errMsg.includes('does not exist') || errMsg.includes('Unable to locate ContractCode') || errMsg.includes('not found')) {
200+
await new Promise(r => setTimeout(r, 3000))
201+
continue
202+
}
203+
break
204+
}
205+
}
206+
207+
if (!isExplorerReady) {
208+
const msg = `Contract not found on ${verifierId} after 30s. Explorer indexing timed out.`
209+
await this.call('terminal', 'log', { type: 'error', value: `[${verifierId}] ${msg}` })
210+
await this.updateReceiptStatus(contractId, verifierId, { status: 'failed', message: msg })
211+
return
212+
}
213+
189214
const timeoutPromise = new Promise((_, reject) =>
190215
setTimeout(() => reject(new Error('Timeout (15s limit exceeded)')), 15000)
191216
)
@@ -213,7 +238,8 @@ export class ContractVerificationPluginClient extends PluginClient {
213238
}
214239
} else if (result.status === 'failed') {
215240
const msg = result.message || 'Unknown failure'
216-
await this.call('terminal', 'log', { type: 'warn', value: `[${verifierId}] Verification Failed: Please open the "Contract Verification" plugin to check details.` })
241+
await this.call('terminal', 'log', { type: 'error', value: `[${verifierId}] Verification Failed: ${msg}` })
242+
await this.call('terminal', 'log', { type: 'warn', value: `[${verifierId}] Please open the "Contract Verification" plugin to retry.` })
217243

218244
if (verifierId === 'Etherscan' && !pluginApiKey) {
219245
await this.call('terminal', 'log', { type: 'warn', value: `Note: To retry Etherscan verification in the plugin, you must save your API key in the plugin settings.` })
@@ -230,7 +256,10 @@ export class ContractVerificationPluginClient extends PluginClient {
230256
message: errorMsg
231257
})
232258

233-
await this.call('terminal', 'log', { type: 'warn', value: `[${verifierId}] Verification Failed: Please open the "Contract Verification" plugin to retry.` })
259+
// [Aniket Feedback]: 에러 메시지 그대로 출력
260+
await this.call('terminal', 'log', { type: 'error', value: `[${verifierId}] Verification Error: ${errorMsg}` })
261+
await this.call('terminal', 'log', { type: 'warn', value: `[${verifierId}] Please open the "Contract Verification" plugin to retry.` })
262+
234263
if (verifierId === 'Etherscan' && !pluginApiKey) {
235264
await this.call('terminal', 'log', { type: 'warn', value: `Note: To retry Etherscan verification in the plugin, you must save your API key in the plugin settings.` })
236265
}

apps/contract-verification/src/app/app.tsx

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const App = () => {
2121
const [themeType, setThemeType] = useState<ThemeType>('dark')
2222
const [settings, setSettings] = useLocalStorage<ContractVerificationSettings>('contract-verification:settings', { chains: {} })
2323
const [submittedContracts, setSubmittedContracts] = useLocalStorage<SubmittedContracts>('contract-verification:submitted-contracts', {})
24-
const [chains, setChains] = useState<Chain[]>([]) // State to hold the chains data
24+
const [chains, setChains] = useState<Chain[]>([])
2525
const [compilationOutput, setCompilationOutput] = useState<{ [key: string]: CompilerAbstract } | undefined>()
2626

2727
// Form values:
@@ -52,28 +52,23 @@ const App = () => {
5252
setLocale(locale)
5353
})
5454

55-
// Fetch compiler artefacts initially
5655
plugin.call('compilerArtefacts' as any, 'getAllCompilerAbstracts').then((obj: any) => {
5756
setCompilationOutput(obj)
5857
})
5958

60-
// Subscribe to compilations
6159
plugin.on('compilerArtefacts' as any, 'compilationSaved', (compilerAbstracts: { [key: string]: CompilerAbstract }) => {
6260
setCompilationOutput((prev) => ({ ...(prev || {}), ...compilerAbstracts }))
6361
})
6462
}
6563

66-
// Check if plugin is already activated
6764
if (plugin.isActivated()) {
6865
initializePlugin()
6966
} else {
70-
// Listen for activation event if not yet activated
7167
plugin.internalEvents.once('verification_activated', () => {
7268
initializePlugin()
7369
})
7470
}
7571

76-
// Fetch chains.json and update state
7772
fetch('https://chainid.network/chains.json')
7873
.then((response) => response.json())
7974
.then((data) => setChains(data))
@@ -87,7 +82,6 @@ const App = () => {
8782
}
8883
plugin.internalEvents.on('submissionUpdated', submissionUpdatedListener)
8984

90-
// Clean up on unmount
9185
return () => {
9286
plugin.off('compilerArtefacts' as any, 'compilationSaved')
9387
plugin.internalEvents.removeListener('submissionUpdated', submissionUpdatedListener)
@@ -98,7 +92,6 @@ const App = () => {
9892
useEffect(() => {
9993
const getPendingReceipts = (submissions: SubmittedContracts) => {
10094
const pendingReceipts: VerificationReceipt[] = []
101-
// Check statuses of receipts
10295
for (const submission of Object.values(submissions)) {
10396
for (const receipt of submission.receipts) {
10497
if (receipt.status === 'pending') {
@@ -124,24 +117,26 @@ const App = () => {
124117

125118
const pollStatus = async () => {
126119
const changedSubmittedContracts = { ...submittedContracts }
120+
const now = new Date().getTime()
127121

128122
for (const receipt of pendingReceipts) {
129-
await new Promise((resolve) => setTimeout(resolve, 500)) // avoid api rate limit exceeding.
123+
await new Promise((resolve) => setTimeout(resolve, 500))
124+
125+
const { verifierInfo, receiptId, contractId } = receipt
126+
127+
const contract = changedSubmittedContracts[contractId]
128+
const submissionTime = new Date(contract.date).getTime()
129+
const isTimedOut = (now - submissionTime) > 30000
130130

131-
const { verifierInfo, receiptId } = receipt
132131
if (receiptId) {
133-
const contract = changedSubmittedContracts[receipt.contractId]
134132
const chainSettings = mergeChainSettingsWithDefaults(contract.chainId, settings)
135133
let verifierSettings = { ...chainSettings.verifiers[verifierInfo.name] }
136134

137-
let usingGlobalKey = false
138-
139135
if (verifierInfo.name === 'Etherscan' && !verifierSettings.apiKey) {
140136
try {
141137
const globalApiKey = await plugin.call('config' as any, 'getAppParameter', 'etherscan-access-token')
142138
if (globalApiKey) {
143139
verifierSettings = { ...verifierSettings, apiKey: globalApiKey }
144-
usingGlobalKey = true
145140
}
146141
} catch (e) { }
147142
}
@@ -175,46 +170,51 @@ const App = () => {
175170
response = await verifier.checkVerificationStatus(receiptId, contract.chainId)
176171
}
177172

178-
if (response.status === 'pending') {
179-
try {
180-
const lookupResult = await verifier.lookup(contract.address, contract.chainId)
181-
if (lookupResult.status === 'verified' || lookupResult.status === 'already verified' || lookupResult.status === 'fully verified') {
182-
response.status = 'verified'
183-
response.lookupUrl = lookupResult.lookupUrl
184-
response.message = 'Verified (Confirmed via Lookup)'
185-
}
186-
} catch (lookupError) {}
187-
}
188173

189174
const { status, message, lookupUrl } = response
190175
const prevStatus = receipt.status
191-
receipt.status = status
192-
receipt.message = message
193-
if (lookupUrl) {
194-
receipt.lookupUrl = lookupUrl
176+
177+
if (status === 'pending' && isTimedOut) {
178+
receipt.status = 'failed'
179+
receipt.message = 'Verification timed out (30s).'
180+
181+
plugin.call('terminal', 'log', { type: 'warn', value: `[${verifierInfo.name}] Polling timed out. Please check the explorer manually.` })
182+
183+
if (contract.address && verifierInfo.name === 'Blockscout') {
184+
plugin.call('terminal', 'log', { type: 'info', value: `Blockscout often verifies silently. Check here: https://eth-sepolia.blockscout.com/address/${contract.address}?tab=contract` })
185+
}
186+
} else {
187+
receipt.status = status
188+
receipt.message = message
189+
if (lookupUrl) {
190+
receipt.lookupUrl = lookupUrl
191+
}
195192
}
196193

197-
if (prevStatus === 'pending' && status !== 'pending') {
194+
if (prevStatus === 'pending' && receipt.status !== 'pending') {
195+
const finalStatus = receipt.status
198196
const successStatuses = ['verified', 'partially verified', 'already verified', 'exactly verified', 'fully verified']
199-
if (successStatuses.includes(status)) {
197+
198+
if (successStatuses.includes(finalStatus)) {
200199
if (receipt.lookupUrl) {
201200
const htmlContent = `<span class="text-success">[${verifierInfo.name}] Verification Successful!</span> &nbsp;<a href="${receipt.lookupUrl}" target="_blank">View Code</a>`
202201
await plugin.call('terminal' as any, 'logHtml', { value: htmlContent })
203202
} else {
204203
const htmlContent = `<span class="text-success">[${verifierInfo.name}] Verification Successful!</span>`
205204
await plugin.call('terminal' as any, 'logHtml', { value: htmlContent })
206205
}
207-
} else if (status === 'failed') {
208-
plugin.call('terminal', 'log', { type: 'warn', value: `[${verifierInfo.name}] Verification Failed: Please open the "Contract Verification" plugin to retry.` })
206+
} else if (finalStatus === 'failed') {
207+
if (receipt.message !== 'Verification timed out (30s).') {
208+
plugin.call('terminal', 'log', { type: 'warn', value: `[${verifierInfo.name}] Verification Failed: Please open the "Contract Verification" plugin to retry.` })
209+
}
209210

210-
if (verifierInfo.name === 'Etherscan' && !chainSettings.verifiers['Etherscan']?.apiKey) {
211-
plugin.call('terminal', 'log', { type: 'warn', value: `Note: To retry Etherscan verification in the plugin, you must save your API key in the plugin settings.` })
212-
}
211+
if (verifierInfo.name === 'Etherscan' && !chainSettings.verifiers['Etherscan']?.apiKey) {
212+
plugin.call('terminal', 'log', { type: 'warn', value: `Note: To retry Etherscan verification in the plugin, you must save your API key in the plugin settings.` })
213+
}
213214
}
214215
}
215216
} catch (e) {
216217
receipt.failedChecks++
217-
// Only retry 10 times
218218
if (receipt.failedChecks >= 10) {
219219
receipt.status = 'failed'
220220
receipt.message = e.message

0 commit comments

Comments
 (0)