diff --git a/packages/hardhat-zksync-node/src/constants.ts b/packages/hardhat-zksync-node/src/constants.ts index 1201a7ba..da813197 100644 --- a/packages/hardhat-zksync-node/src/constants.ts +++ b/packages/hardhat-zksync-node/src/constants.ts @@ -23,8 +23,8 @@ export const ALLOWED_SHOW_VM_DETAILS_VALUES = ['none', 'all']; export const ALLOWED_SHOW_GAS_DETAILS_VALUES = ['none', 'all']; export const DEFAULT_RELEASE_VERSION_INFO_CACHE_PERIOD = 24 * 60 * 60 * 1000; // 24 hours -export const DEFAULT_RELEASE_CACHE_FILE_NAME = 'latestRelease.json'; - +export const DEFAULT_RELEASE_CACHE_FILE_NAME = 'list.json'; +export const ERA_TEST_NODE_BINARY_VERSION = '0.1.0'; export const PLATFORM_MAP: Record = { darwin: 'apple-darwin', linux: 'unknown-linux-gnu', @@ -53,6 +53,8 @@ export const NETWORK_ETH = { LOCALHOST: 'localhost', }; +export const DEFAULT_ZKSYNC_ANVIL_VERSION = '0.2.*'; + export const DEFAULT_TIMEOUT_MILISECONDS = 30000; // export const TOOLCHAIN_MAP: Record = { diff --git a/packages/hardhat-zksync-node/src/core/global-interceptor.ts b/packages/hardhat-zksync-node/src/core/global-interceptor.ts index 862b7db0..c411a61e 100644 --- a/packages/hardhat-zksync-node/src/core/global-interceptor.ts +++ b/packages/hardhat-zksync-node/src/core/global-interceptor.ts @@ -32,7 +32,7 @@ async function wrapTaskWithNode(taskArgs: TaskArguments, env: any, runSuper: Run return await runSuper(taskArgs); } const zkSyncGlobal = global as ZKSyncTasksWithWrappedNode; - const { commandArgs, server, port } = await startServer(); + const { commandArgs, server, port } = await startServer(env.config.zksyncAnvil.version); try { await server.listen(commandArgs, false); await waitForNodeToBeReady(port); diff --git a/packages/hardhat-zksync-node/src/core/script-runner.ts b/packages/hardhat-zksync-node/src/core/script-runner.ts index 69c96ec0..8ad58112 100644 --- a/packages/hardhat-zksync-node/src/core/script-runner.ts +++ b/packages/hardhat-zksync-node/src/core/script-runner.ts @@ -6,6 +6,7 @@ import { startServer, waitForNodeToBeReady } from '../utils'; export async function runScript( scriptPath: string, + zksyncAnvilVersion: string, scriptArgs: string[] = [], extraNodeArgs: string[] = [], extraEnvVars: { [name: string]: string } = {}, @@ -19,7 +20,7 @@ export async function runScript( ...extraNodeArgs, ]; - const { commandArgs, server, port } = await startServer(); + const { commandArgs, server, port } = await startServer(zksyncAnvilVersion); await server.listen(commandArgs, false); await waitForNodeToBeReady(port); @@ -46,15 +47,22 @@ export async function runScript( export async function runScriptWithHardhat( hardhatArguments: HardhatArguments, + zksyncAnvilVersion: string, scriptPath: string, scriptArgs: string[] = [], extraNodeArgs: string[] = [], extraEnvVars: { [name: string]: string } = {}, ): Promise { - return runScript(scriptPath, scriptArgs, [...extraNodeArgs, '--require', path.join(__dirname, 'register')], { - ...getEnvVariablesMap(hardhatArguments), - ...extraEnvVars, - }); + return runScript( + scriptPath, + zksyncAnvilVersion, + scriptArgs, + [...extraNodeArgs, '--require', path.join(__dirname, 'register')], + { + ...getEnvVariablesMap(hardhatArguments), + ...extraEnvVars, + }, + ); } function withFixedInspectArg(argv: string[]) { diff --git a/packages/hardhat-zksync-node/src/downloader.ts b/packages/hardhat-zksync-node/src/downloader.ts index 5a669128..e9c1c152 100644 --- a/packages/hardhat-zksync-node/src/downloader.ts +++ b/packages/hardhat-zksync-node/src/downloader.ts @@ -1,7 +1,7 @@ import path from 'path'; import fse from 'fs-extra'; import chalk from 'chalk'; -import { download, getLatestRelease, getNodeUrl } from './utils'; +import { download, getAllTags, getLatestRelease, getNodeUrl, resolveTag } from './utils'; import { ZkSyncNodePluginError } from './errors'; import { DEFAULT_RELEASE_CACHE_FILE_NAME, @@ -15,76 +15,41 @@ import { } from './constants'; export class RPCServerDownloader { + private readonly _tagRegex: RegExp = /^\d+\.\d+\.(\d+)(-[A-Za-z0-9.]+)?$|^\d+\.\d+\.\*(?!-)[A-Za-z0-9.]*$/; private readonly _binaryDir: string; - private readonly _tag: string; - private readonly _releaseInfoFile: string = DEFAULT_RELEASE_CACHE_FILE_NAME; - private readonly _releaseInfoFilePath: string; + private _tag?: string; + private readonly _initialTag: string; + private readonly _tagsInfoFile: string = DEFAULT_RELEASE_CACHE_FILE_NAME; + private readonly _tagsInfoFilePath: string; - constructor(binaryDir: string, tag: string) { + constructor(binaryDir: string, initialTag: string) { this._binaryDir = binaryDir; - this._tag = tag; - this._releaseInfoFilePath = path.join(this._binaryDir, this._releaseInfoFile); + if (!this._tagRegex.test(initialTag) && initialTag !== 'latest') { + throw new ZkSyncNodePluginError(`Invalid tag format: ${initialTag}`); + } + this._initialTag = initialTag; + this._tagsInfoFilePath = path.join(this._binaryDir, this._tagsInfoFile); } public async downloadIfNeeded(force: boolean): Promise { + if (!(await this._isTagsInfoValid()) || force) { + await this._downloadTagInfo(); + } + + const tagsInfo = await this._getTagsInfo(); + this._tag = resolveTag(tagsInfo.tags, tagsInfo.latest, this._initialTag); + if (force) { - const releaseTag = this._isLatestTag() - ? await getLatestRelease( - ZKNODE_BIN_OWNER, - ZKNODE_BIN_REPOSITORY_NAME, - USER_AGENT, - DEFAULT_TIMEOUT_MILISECONDS, - ) - : this._tag; - await this._download(releaseTag); + await this._download(this._tag); return; } - if (this._isLatestTag()) { - if (!(await this._isLatestReleaseInfoValid())) { - const latestTag = await getLatestRelease( - ZKNODE_BIN_OWNER, - ZKNODE_BIN_REPOSITORY_NAME, - USER_AGENT, - DEFAULT_TIMEOUT_MILISECONDS, - ); - - if (await this._isBinaryPathExists(latestTag)) { - await this._postProcessDownload(latestTag); - return; - } - - await this._download(latestTag); - return; - } - - const info = await this._getLatestReleaseInfo(); - if (info && (await this._isBinaryPathExists(info.latest))) { - return; - } - - const latestTagForLatestRelease = await getLatestRelease( - ZKNODE_BIN_OWNER, - ZKNODE_BIN_REPOSITORY_NAME, - USER_AGENT, - DEFAULT_TIMEOUT_MILISECONDS, - ); - - if ( - info && - info.latest === latestTagForLatestRelease && - (await this._isBinaryPathExists(latestTagForLatestRelease)) - ) { - await this._postProcessDownload(latestTagForLatestRelease); - return; - } - await this._download(latestTagForLatestRelease); + if (await this._isBinaryPathExists(this._tag)) { + await this._postProcessDownload(this._tag); return; } - if (!(await this._isBinaryPathExists(this._tag))) { - await this._download(this._tag); - } + await this._download(this._tag); } private async _download(tag: any): Promise { @@ -99,12 +64,15 @@ export class RPCServerDownloader { } } - private async _isBinaryPathExists(version?: string): Promise { - return fse.existsSync(await this.getBinaryPath(version)); + private async _isBinaryPathExists(tag?: string): Promise { + return fse.existsSync(await this.getBinaryPath(tag)); } - public async getBinaryPath(version?: string): Promise { - return path.join(this._binaryDir, version || (await this._getReleaseTag())); + public async getBinaryPath(tag?: string): Promise { + if (!tag && !this._tag) { + throw new ZkSyncNodePluginError('Tag is not set'); + } + return path.join(this._binaryDir, tag || this._tag!); } private async _createBinaryPath(version: string): Promise { @@ -114,40 +82,44 @@ export class RPCServerDownloader { private async _postProcessDownload(tag: string): Promise { const binaryPath = await this.getBinaryPath(tag); fse.chmodSync(binaryPath, 0o755); - - if (this._isLatestTag()) { - await fse.writeJSON(this._releaseInfoFilePath, { latest: tag }); - } - } - - private async _getReleaseTag() { - return this._isLatestTag() ? (await this._getLatestReleaseInfo()).latest : this._tag; } - private async _isLatestReleaseInfoValid() { - if (!fse.existsSync(this._releaseInfoFilePath)) { + private async _isTagsInfoValid() { + if (!(await this._isTagsInfoExists())) { return false; } - const stats = await fse.stat(this._releaseInfoFilePath); + const stats = await fse.stat(this._tagsInfoFilePath); const age = new Date().valueOf() - stats.ctimeMs; return age < DEFAULT_RELEASE_VERSION_INFO_CACHE_PERIOD; } - private async _isLatestReleaseInfoExists() { - return fse.existsSync(this._releaseInfoFilePath); + private async _isTagsInfoExists() { + return fse.existsSync(this._tagsInfoFilePath); } - private async _getLatestReleaseInfo() { - if (!(await this._isLatestReleaseInfoExists())) { + private async _getTagsInfo() { + if (!(await this._isTagsInfoValid())) { return undefined; } - return await fse.readJSON(this._releaseInfoFilePath); + return await fse.readJSON(this._tagsInfoFilePath); } - private _isLatestTag() { - return this._tag === 'latest'; + private async _downloadTagInfo() { + const allTags = await getAllTags( + ZKNODE_BIN_OWNER, + ZKNODE_BIN_REPOSITORY_NAME, + USER_AGENT, + DEFAULT_TIMEOUT_MILISECONDS, + ); + const latestTag = await getLatestRelease( + ZKNODE_BIN_OWNER, + ZKNODE_BIN_REPOSITORY_NAME, + USER_AGENT, + DEFAULT_TIMEOUT_MILISECONDS, + ); + await fse.writeJSON(this._tagsInfoFilePath, { tags: allTags, latest: latestTag }); } } diff --git a/packages/hardhat-zksync-node/src/index.ts b/packages/hardhat-zksync-node/src/index.ts index 0ce43029..ed79426c 100644 --- a/packages/hardhat-zksync-node/src/index.ts +++ b/packages/hardhat-zksync-node/src/index.ts @@ -1,5 +1,5 @@ import { spawn } from 'child_process'; -import { task, subtask, types } from 'hardhat/config'; +import { task, subtask, types, extendConfig } from 'hardhat/config'; import { TASK_COMPILE, TASK_NODE, @@ -13,6 +13,7 @@ import { HARDHAT_NETWORK_NAME } from 'hardhat/plugins'; import { TaskArguments } from 'hardhat/types'; import path from 'path'; import { + DEFAULT_ZKSYNC_ANVIL_VERSION, MAX_PORT_ATTEMPTS, START_PORT, TASK_NODE_ZKSYNC, @@ -35,13 +36,19 @@ import { ZkSyncNodePluginError } from './errors'; import { interceptAndWrapTasksWithNode } from './core/global-interceptor'; import { runScriptWithHardhat } from './core/script-runner'; +extendConfig((config, userConfig) => { + config.zksyncAnvil = { + version: userConfig.zksyncAnvil?.version || DEFAULT_ZKSYNC_ANVIL_VERSION, + }; +}); + task(TASK_RUN).setAction(async (args, hre, runSuper) => { if (!hre.network.zksync || hre.network.name !== HARDHAT_NETWORK_NAME) { await runSuper(args, hre); return; } - await runScriptWithHardhat(hre.hardhatArguments, path.resolve(args.script)); + await runScriptWithHardhat(hre.hardhatArguments, hre.config.zksyncAnvil.version!, path.resolve(args.script)); }); // Subtask to download the binary @@ -61,9 +68,9 @@ subtask(TASK_NODE_ZKSYNC_DOWNLOAD_BINARY, 'Downloads the JSON-RPC server binary' ) => { // Directory where the binaries are stored const rpcServerBinaryDir = await getRPCServerBinariesDir(); - + const version = tag || _hre.config.zksyncAnvil.version!; // Get the latest release of the binary - const downloader: RPCServerDownloader = new RPCServerDownloader(rpcServerBinaryDir, tag || 'latest'); + const downloader: RPCServerDownloader = new RPCServerDownloader(rpcServerBinaryDir, version); // Download binary if needed await downloader.downloadIfNeeded(force); diff --git a/packages/hardhat-zksync-node/src/type-extensions.ts b/packages/hardhat-zksync-node/src/type-extensions.ts index e6331033..5b64eaf0 100644 --- a/packages/hardhat-zksync-node/src/type-extensions.ts +++ b/packages/hardhat-zksync-node/src/type-extensions.ts @@ -1,6 +1,14 @@ import 'hardhat/types/config'; +import { ZkSyncAnvilConfig } from './types'; declare module 'hardhat/types/config' { + interface HardhatUserConfig { + zksyncAnvil?: Partial; + } + + interface HardhatConfig { + zksyncAnvil: ZkSyncAnvilConfig; + } interface HardhatNetworkUserConfig { zksync?: boolean; ethNetwork?: string; diff --git a/packages/hardhat-zksync-node/src/types.ts b/packages/hardhat-zksync-node/src/types.ts index de9014c1..289b4467 100644 --- a/packages/hardhat-zksync-node/src/types.ts +++ b/packages/hardhat-zksync-node/src/types.ts @@ -15,3 +15,7 @@ export interface CommandArguments { forkBlockNumber?: number; replayTx?: string; } + +export interface ZkSyncAnvilConfig { + version?: string; +} diff --git a/packages/hardhat-zksync-node/src/utils.ts b/packages/hardhat-zksync-node/src/utils.ts index b41ef1f3..15ecccc2 100644 --- a/packages/hardhat-zksync-node/src/utils.ts +++ b/packages/hardhat-zksync-node/src/utils.ts @@ -20,6 +20,7 @@ import { ALLOWED_SHOW_STORAGE_LOGS_VALUES, ALLOWED_SHOW_VM_DETAILS_VALUES, BASE_URL, + ERA_TEST_NODE_BINARY_VERSION, MAX_PORT_ATTEMPTS, NETWORK_ACCOUNTS, NETWORK_ETH, @@ -155,14 +156,20 @@ export async function getRPCServerBinariesDir(): Promise { return rpcServerBinariesPath; } -// Get latest release from GitHub of the anvil-zksync binary export async function getLatestRelease(owner: string, repo: string, userAgent: string, timeout: number): Promise { - const url = `https://github.com/${owner}/${repo}/releases/latest`; - const redirectUrlPattern = `https://github.com/${owner}/${repo}/releases/tag/v`; + const finalUrl = await handleRedirect(`https://github.com/${owner}/${repo}/releases/latest`, userAgent, timeout); + const match = finalUrl.match(/\/releases\/tag\/v(.*)/); + if (match) { + return match[1]; + } + throw new ZkSyncNodePluginError(`Couldn't find the latest release for URL: ${finalUrl}`); +} - const { request } = await import('undici'); +export async function getAllTags(owner: string, repo: string, userAgent: string, timeout: number): Promise { + const finalUrl = await handleRedirect(`https://api.github.com/repos/${owner}/${repo}/tags`, userAgent, timeout); - const response = await request(url, { + const { request } = await import('undici'); + const response = await request(finalUrl, { headersTimeout: timeout, maxRedirections: 0, method: 'GET', @@ -171,27 +178,52 @@ export async function getLatestRelease(owner: string, repo: string, userAgent: s }, }); - // Check if the response is a redirect - if (response.statusCode >= 300 && response.statusCode < 400) { - // Get the URL from the 'location' header - if (response.headers.location && typeof response.headers.location === 'string') { - // Check if the redirect URL matches the expected pattern - if (response.headers.location.startsWith(redirectUrlPattern)) { - // Extract the tag from the redirect URL - return response.headers.location.substring(redirectUrlPattern.length); - } + if (response.statusCode === 200) { + return JSON.parse(await response.body.text()).map((tag: any) => tag.name.slice(1)); + } else { + throw new ZkSyncNodePluginError(`Unexpected response status: ${response.statusCode} for URL: ${finalUrl}`); + } +} + +async function handleRedirect(url: string, userAgent: string, timeout: number): Promise { + const { request } = await import('undici'); + + let currentUrl = url; - throw new ZkSyncNodePluginError(`Unexpected redirect URL: ${response.headers.location} for URL: ${url}`); + while (true) { + const response = await request(currentUrl, { + headersTimeout: timeout, + maxRedirections: 0, + method: 'GET', + headers: { + 'User-Agent': `${userAgent}`, + }, + }); + + if (response.statusCode >= 300 && response.statusCode < 400) { + if (response.headers.location && typeof response.headers.location === 'string') { + currentUrl = response.headers.location; + continue; + } else { + throw new ZkSyncNodePluginError(`Redirect location not found for URL: ${currentUrl}`); + } } else { - // Throw an error if the 'location' header is missing in a redirect response - throw new ZkSyncNodePluginError(`Redirect location not found for URL: ${url}`); + return currentUrl; } - } else { - // Throw an error for non-redirect responses - throw new ZkSyncNodePluginError(`Unexpected response status: ${response.statusCode} for URL: ${url}`); } } +export function resolveTag(tags: string[], latestTag: string, initialTag: string): string { + if (initialTag === 'latest') { + return latestTag; + } + const [major, minor, patch] = initialTag.split('.'); + const tag = tags.find((t) => t.startsWith(patch === '*' ? `${major}.${minor}` : initialTag)); + if (!tag) { + throw new ZkSyncNodePluginError(`No release found for ${initialTag}`); + } + return tag; +} // Get the asset to download from the latest release of the anvil-zksync binary export async function getNodeUrl(repo: string, release: string): Promise { const platform = getPlatform(); @@ -201,7 +233,7 @@ export async function getNodeUrl(repo: string, release: string): Promise throw new ZkSyncNodePluginError(`Unsupported platform: ${platform}`); } - return semver.gte(release, '0.1.0') + return semver.gte(release, ERA_TEST_NODE_BINARY_VERSION) ? `${repo}/releases/download/v${release}/anvil-zksync-v${release}-${getArch()}-${platform}.tar.gz` : `${repo}/releases/download/v${release}/era_test_node-v${release}-${getArch()}-${platform}.tar.gz`; } diff --git a/packages/hardhat-zksync-node/test/tests.ts b/packages/hardhat-zksync-node/test/tests.ts index dc77320c..10caaea7 100644 --- a/packages/hardhat-zksync-node/test/tests.ts +++ b/packages/hardhat-zksync-node/test/tests.ts @@ -85,7 +85,7 @@ describe('node-zksync plugin', async function () { assert.fail('Expected an error to be thrown'); } catch (error: any) { expect(error.message).to.be.equal( - 'Unexpected response status: 404 for URL: https://github.com/owner/repo/releases/latest', + "Couldn't find the latest release for URL: https://github.com/owner/repo/releases/latest", ); } }); @@ -139,7 +139,7 @@ describe('node-zksync plugin', async function () { assert.fail('Expected an error to be thrown'); } catch (error: any) { expect(error.message).to.be.equal( - 'Unexpected redirect URL: https://github.com/owner/wrong/ for URL: https://github.com/owner/repo/releases/latest', + "Couldn't find the latest release for URL: https://github.com/owner/wrong/", ); } }); @@ -352,13 +352,23 @@ describe('node-zksync plugin', async function () { let existsSyncStub: sinon.SinonStub; let postProcessDownloadStub: sinon.SinonStub; let releaseStub: sinon.SinonStub; + let getAllTagsStub: sinon.SinonStub; let urlStub: sinon.SinonStub; + let isTagsInfoExistsStub: sinon.SinonStub; + let isTagsInfoValidStub: sinon.SinonStub; + let getTagsInfoStub: sinon.SinonStub; + let downloadTagInfoStub: sinon.SinonStub; beforeEach(() => { downloadStub = sinon.stub(utils, 'download'); releaseStub = sinon.stub(utils, 'getLatestRelease'); + getAllTagsStub = sinon.stub(utils, 'getAllTags'); urlStub = sinon.stub(utils, 'getNodeUrl'); existsSyncStub = sinon.stub(fs, 'existsSync'); + isTagsInfoExistsStub = sinon.stub(RPCServerDownloader.prototype as any, '_isTagsInfoExists'); + isTagsInfoValidStub = sinon.stub(RPCServerDownloader.prototype as any, '_isTagsInfoValid'); + getTagsInfoStub = sinon.stub(RPCServerDownloader.prototype as any, '_getTagsInfo'); + downloadTagInfoStub = sinon.stub(RPCServerDownloader.prototype as any, '_downloadTagInfo'); postProcessDownloadStub = sinon .stub(RPCServerDownloader.prototype as any, '_postProcessDownload') .resolves(); // Stubbing the private method @@ -373,15 +383,40 @@ describe('node-zksync plugin', async function () { const downloader = new RPCServerDownloader('../cache/node', 'latest'); existsSyncStub.resolves(false); + isTagsInfoExistsStub.resolves(true); + isTagsInfoValidStub.resolves(true); + getTagsInfoStub.resolves({ tags: ['v1.0.0', 'v1.0.1', 'v1.0.2'], latest: 'v1.0.2' }); releaseStub.resolves(mockRelease); urlStub.resolves(mockUrl); - downloadStub.resolves('v1.0.0'); + getAllTagsStub.resolves(['v1.0.0', 'v1.0.1', 'v1.0.2']); + downloadStub.resolves('v1.0.2'); await downloader.downloadIfNeeded(false); + sinon.assert.calledOnce(getTagsInfoStub); + sinon.assert.calledOnce(downloadStub); + sinon.assert.calledOnce(postProcessDownloadStub); + }); + + it('should download the binary if not already downloaded without list.json', async function () { + const downloader = new RPCServerDownloader('../cache/node', 'latest'); + + existsSyncStub.resolves(false); + isTagsInfoExistsStub.resolves(false); + isTagsInfoValidStub.resolves(false); + getTagsInfoStub.resolves({ tags: ['v1.0.0', 'v1.0.1', 'v1.0.2'], latest: 'v1.0.2' }); + releaseStub.resolves(mockRelease); + urlStub.resolves(mockUrl); + getAllTagsStub.resolves(['v1.0.0', 'v1.0.1', 'v1.0.2']); + downloadStub.resolves('v1.0.2'); + downloadTagInfoStub.resolves(); + + await downloader.downloadIfNeeded(false); + + sinon.assert.calledOnce(downloadTagInfoStub); + sinon.assert.calledOnce(getTagsInfoStub); sinon.assert.calledOnce(downloadStub); sinon.assert.calledOnce(postProcessDownloadStub); - sinon.assert.calledOnce(releaseStub); }); it('should force download the binary', async function () { @@ -390,13 +425,18 @@ describe('node-zksync plugin', async function () { existsSyncStub.resolves(false); releaseStub.resolves(mockRelease); urlStub.resolves(mockUrl); - downloadStub.resolves('v1.0.0'); + isTagsInfoExistsStub.resolves(true); + isTagsInfoValidStub.resolves(true); + getTagsInfoStub.resolves({ tags: ['v1.0.0', 'v1.0.1', 'v1.0.2'], latest: 'v1.0.2' }); + getAllTagsStub.resolves(['v1.0.0', 'v1.0.1', 'v1.0.2']); + downloadStub.resolves('v1.0.2'); + downloadTagInfoStub.resolves(); await downloader.downloadIfNeeded(true); sinon.assert.calledOnce(downloadStub); sinon.assert.calledOnce(postProcessDownloadStub); - sinon.assert.calledOnce(releaseStub); + sinon.assert.calledOnce(downloadTagInfoStub); }); it('should throw an error if download fails', async function () { @@ -405,6 +445,10 @@ describe('node-zksync plugin', async function () { downloadStub.throws(new Error('Mocked download failure')); existsSyncStub.resolves(false); releaseStub.resolves(mockRelease); + isTagsInfoExistsStub.resolves(true); + isTagsInfoValidStub.resolves(true); + getTagsInfoStub.resolves({ tags: ['v1.0.0', 'v1.0.1', 'v1.0.2'], latest: 'v1.0.2' }); + getAllTagsStub.resolves(['v1.0.0', 'v1.0.1', 'v1.0.2']); urlStub.resolves(mockRelease); try { @@ -441,7 +485,7 @@ describe('node-zksync plugin', async function () { describe('listen', async function () { it('should start the JSON-RPC server with the provided arguments', async function () { - const server = new JsonRpcServer('/path/to/binary'); + const server = new JsonRpcServer('/path/to/binary', 'latest'); const args = ['--arg1=value1', '--arg2=value2']; server.listen(args); @@ -450,14 +494,14 @@ describe('node-zksync plugin', async function () { }); it('should print a starting message when server starts', () => { - const server = new JsonRpcServer('/path/to/binary'); + const server = new JsonRpcServer('/path/to/binary', 'latest'); server.listen(); sinon.assert.calledWith(consoleInfoStub, chalk.green('Starting the JSON-RPC server at 127.0.0.1:8011')); }); it.skip('should handle termination signals gracefully', async function () { - const server = new JsonRpcServer('/path/to/binary'); + const server = new JsonRpcServer('/path/to/binary', 'latest'); const error = new Error('Mocked error') as SpawnSyncError; error.signal = PROCESS_TERMINATION_SIGNALS[0]; // Let's simulate the first signal, e.g., 'SIGINT' spawnStub.throws(error); @@ -476,7 +520,7 @@ describe('node-zksync plugin', async function () { }); it('should throw an error if the server process exits with an error', async function () { - const server = new JsonRpcServer('/path/to/binary'); + const server = new JsonRpcServer('/path/to/binary', 'latest'); const error = new Error('Mocked error'); spawnStub.throws(error);