diff --git a/packages/hardhat-zksync-verify/src/constants.ts b/packages/hardhat-zksync-verify/src/constants.ts index dc5834e11..d31204805 100644 --- a/packages/hardhat-zksync-verify/src/constants.ts +++ b/packages/hardhat-zksync-verify/src/constants.ts @@ -122,4 +122,8 @@ export const COMPILATION_ERRORS = [ error: 'MissingContract', pattern: /^Backend verification error: Contract with .* name is missing in sources$/, }, + { + error: 'DeployedBytecodeMismatch', + pattern: /^Backend verification error: Deployed bytecode is not equal to generated one from given source$/, + }, ]; diff --git a/packages/hardhat-zksync-verify/src/plugin.ts b/packages/hardhat-zksync-verify/src/plugin.ts index 294161618..bc2ba01fd 100644 --- a/packages/hardhat-zksync-verify/src/plugin.ts +++ b/packages/hardhat-zksync-verify/src/plugin.ts @@ -1,5 +1,10 @@ -import { TASK_FLATTEN_GET_FLATTENED_SOURCE } from 'hardhat/builtin-tasks/task-names'; -import { Artifacts, CompilerInput, HardhatRuntimeEnvironment, ResolvedFile } from 'hardhat/types'; +import { + TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, + TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, + TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS, + TASK_FLATTEN_GET_FLATTENED_SOURCE, +} from 'hardhat/builtin-tasks/task-names'; +import { Artifacts, CompilerInput, DependencyGraph, HardhatRuntimeEnvironment, ResolvedFile } from 'hardhat/types'; import { isFullyQualifiedName, parseFullyQualifiedName } from 'hardhat/utils/contract-names'; import path from 'path'; import chalk from 'chalk'; @@ -69,6 +74,23 @@ Instead, this name was received: ${contractFQN}`, } } +export async function getMinimalResolvedFiles( + hre: HardhatRuntimeEnvironment, + sourceName: string, +): Promise { + hre.config.zksolc.settings.contractsToCompile = [sourceName]; + const sourcePaths = await hre.run(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS, { sourcePath: hre.config.paths.sources }); + const sourceNames = await hre.run(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, { + rootPath: hre.config.paths.root, + sourcePaths, + }); + const dependencyGraph: DependencyGraph = await hre.run(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, { + sourceNames, + }); + + return dependencyGraph.getResolvedFiles(); +} + export function getSolidityStandardJsonInput( hre: HardhatRuntimeEnvironment, resolvedFiles: ResolvedFile[], diff --git a/packages/hardhat-zksync-verify/src/task-actions.ts b/packages/hardhat-zksync-verify/src/task-actions.ts index 0aec210ae..7749f03d0 100644 --- a/packages/hardhat-zksync-verify/src/task-actions.ts +++ b/packages/hardhat-zksync-verify/src/task-actions.ts @@ -1,9 +1,8 @@ -import { DependencyGraph, HardhatRuntimeEnvironment, RunSuperFunction, TaskArguments } from 'hardhat/types'; +import { HardhatRuntimeEnvironment, RunSuperFunction, TaskArguments } from 'hardhat/types'; import { parseFullyQualifiedName } from 'hardhat/utils/contract-names'; import chalk from 'chalk'; import path from 'path'; -import { TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH } from 'hardhat/builtin-tasks/task-names'; import { getSupportedCompilerVersions, verifyContractRequest } from './zksync-block-explorer/service'; import { @@ -33,7 +32,13 @@ import { ZkSyncVerifyPluginError } from './errors'; import { Bytecode, extractMatchingContractInformation } from './solc/bytecode'; import { ContractInformation } from './solc/types'; -import { checkContractName, getLibraries, getSolidityStandardJsonInput, inferContractArtifacts } from './plugin'; +import { + checkContractName, + getLibraries, + getMinimalResolvedFiles, + getSolidityStandardJsonInput, + inferContractArtifacts, +} from './plugin'; import { SolcMultiUserConfigExtractor, SolcSoloUserConfigExtractor, @@ -203,15 +208,11 @@ export async function verifyContract( contractInformation.contractName = `${contractInformation.sourceName}:${contractInformation.contractName}`; - const dependencyGraph: DependencyGraph = await hre.run(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, { - sourceNames: [contractInformation.sourceName], - }); - const request = { contractAddress: address, sourceCode: getSolidityStandardJsonInput( hre, - dependencyGraph.getResolvedFiles(), + await getMinimalResolvedFiles(hre, contractInformation.sourceName), contractInformation.compilerInput, ), codeFormat: JSON_INPUT_CODE_FORMAT, diff --git a/packages/hardhat-zksync-verify/src/utils.ts b/packages/hardhat-zksync-verify/src/utils.ts index 2cd6b9317..8b079ebe8 100644 --- a/packages/hardhat-zksync-verify/src/utils.ts +++ b/packages/hardhat-zksync-verify/src/utils.ts @@ -45,11 +45,20 @@ export async function encodeArguments(abi: any, constructorArgs: any[]) { return deployArgumentsEncoded; } +export function nextAttemptDelay(currentAttempt: number, baseDelay: number, baseNumberOfAttempts: number): number { + if (currentAttempt < baseNumberOfAttempts) { + return baseDelay; + } + + return baseDelay * 2 ** (currentAttempt - baseNumberOfAttempts); +} + export async function executeVeificationWithRetry( requestId: number, verifyURL: string, - maxRetries = 5, - delayInMs = 1500, + maxRetries = 11, + baseRetries = 5, + baseDelayInMs = 2000, ): Promise { let retries = 0; @@ -63,6 +72,8 @@ export async function executeVeificationWithRetry( console.info(chalk.cyan(PENDING_CONTRACT_INFORMATION_MESSAGE(requestId))); return; } + + const delayInMs = nextAttemptDelay(retries, baseDelayInMs, baseRetries); await delay(delayInMs); } } diff --git a/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts b/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts index ba0710828..3fc9bf747 100644 --- a/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts +++ b/packages/hardhat-zksync-verify/test/tests/task-actions.test.ts @@ -1,7 +1,12 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { fail } from 'assert'; -import { TASK_COMPILE, TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH } from 'hardhat/builtin-tasks/task-names'; +import { + TASK_COMPILE, + TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH, + TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES, + TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS, +} from 'hardhat/builtin-tasks/task-names'; import { getCompilerVersions, getConstructorArguments, @@ -121,6 +126,17 @@ describe('verifyContract', async function () { const runSuperStub = sinon.stub().resolves(0); const hre = { + config: { + paths: { + sources: 'contracts', + root: 'root', + }, + zksolc: { + settings: { + contractsToCompile: [], + }, + }, + }, network: { config: { url: 'http://localhost:3000', @@ -178,6 +194,10 @@ describe('verifyContract', async function () { solcVersion: '0.8.0', }) .onCall(3) + .resolves(['contracts/']) + .onCall(4) + .resolves(['contracts/Contract.sol']) + .onCall(5) .resolves({ getResolvedFiles: sinon.stub().resolves(), }), @@ -188,8 +208,10 @@ describe('verifyContract', async function () { expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS); expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE); expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION); - expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); - expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS); + expect(hre.run.getCall(4).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES); + expect(hre.run.getCall(5).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); + expect(hre.run.getCall(6).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); }); it('should call fallback request sent if there is compilation error', async function () { @@ -209,6 +231,17 @@ describe('verifyContract', async function () { const runSuperStub = sinon.stub().resolves(0); const hre = { + config: { + paths: { + sources: 'contracts', + root: 'root', + }, + zksolc: { + settings: { + contractsToCompile: [], + }, + }, + }, network: { config: { url: 'http://localhost:3000', @@ -266,12 +299,16 @@ describe('verifyContract', async function () { solcVersion: '0.8.0', }) .onCall(3) + .resolves(['contracts/']) + .onCall(4) + .resolves(['contracts/Contract.sol']) + .onCall(5) .resolves({ getResolvedFiles: sinon.stub().resolves(), }) - .onCall(4) + .onCall(6) .throwsException(new ZkSyncVerifyPluginError(errorMessage)) - .onCall(5) + .onCall(7) .resolves(true), }; @@ -280,9 +317,11 @@ describe('verifyContract', async function () { expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS); expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE); expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION); - expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); - expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); - expect(hre.run.getCall(5).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS); + expect(hre.run.getCall(4).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES); + expect(hre.run.getCall(5).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); + expect(hre.run.getCall(6).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(7).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); }); it('should call fallback request sent if there is missing source file', async function () { @@ -301,6 +340,17 @@ describe('verifyContract', async function () { const runSuperStub = sinon.stub().resolves(0); const hre = { + config: { + paths: { + sources: 'contracts', + root: 'root', + }, + zksolc: { + settings: { + contractsToCompile: [], + }, + }, + }, network: { config: { url: 'http://localhost:3000', @@ -358,12 +408,16 @@ describe('verifyContract', async function () { solcVersion: '0.8.0', }) .onCall(3) + .resolves(['contracts/']) + .onCall(4) + .resolves(['contracts/Contract.sol']) + .onCall(5) .resolves({ getResolvedFiles: sinon.stub().resolves(), }) - .onCall(4) + .onCall(6) .throwsException(new ZkSyncVerifyPluginError(errorMessage)) - .onCall(5) + .onCall(7) .resolves(true), }; @@ -372,9 +426,11 @@ describe('verifyContract', async function () { expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS); expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE); expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION); - expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); - expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); - expect(hre.run.getCall(5).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS); + expect(hre.run.getCall(4).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES); + expect(hre.run.getCall(5).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); + expect(hre.run.getCall(6).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(7).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); }); it('should call fallback request sent if there is missing contract file', async function () { @@ -394,6 +450,17 @@ describe('verifyContract', async function () { const runSuperStub = sinon.stub().resolves(0); const hre = { + config: { + paths: { + sources: 'contracts', + root: 'root', + }, + zksolc: { + settings: { + contractsToCompile: [], + }, + }, + }, network: { config: { url: 'http://localhost:3000', @@ -451,12 +518,16 @@ describe('verifyContract', async function () { solcVersion: '0.8.0', }) .onCall(3) + .resolves(['contracts/']) + .onCall(4) + .resolves(['contracts/Contract.sol']) + .onCall(5) .resolves({ getResolvedFiles: sinon.stub().resolves(), }) - .onCall(4) + .onCall(6) .throwsException(new ZkSyncVerifyPluginError(errorMessage)) - .onCall(5) + .onCall(7) .resolves(true), }; @@ -466,9 +537,11 @@ describe('verifyContract', async function () { expect(hre.run.firstCall.args[0]).to.equal(TASK_VERIFY_GET_COMPILER_VERSIONS); expect(hre.run.secondCall.args[0]).to.equal(TASK_COMPILE); expect(hre.run.thirdCall.args[0]).to.equal(TASK_VERIFY_GET_CONTRACT_INFORMATION); - expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); - expect(hre.run.getCall(4).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); - expect(hre.run.getCall(5).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(3).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS); + expect(hre.run.getCall(4).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_SOURCE_NAMES); + expect(hre.run.getCall(5).args[0]).to.equal(TASK_COMPILE_SOLIDITY_GET_DEPENDENCY_GRAPH); + expect(hre.run.getCall(6).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); + expect(hre.run.getCall(7).args[0]).to.equal(TASK_CHECK_VERIFICATION_STATUS); }); }); describe('getCompilerVersions', async function () {