diff --git a/yarn-project/ethereum/package.json b/yarn-project/ethereum/package.json index 87ecc3d321ea..5081cab89dc3 100644 --- a/yarn-project/ethereum/package.json +++ b/yarn-project/ethereum/package.json @@ -33,7 +33,6 @@ "@aztec/l1-artifacts": "workspace:^", "@viem/anvil": "^0.0.10", "dotenv": "^16.0.3", - "get-port": "^7.1.0", "tslib": "^2.4.0", "viem": "^2.7.15", "zod": "^3.23.8" diff --git a/yarn-project/ethereum/src/test/start_anvil.test.ts b/yarn-project/ethereum/src/test/start_anvil.test.ts index 8efdff4452b3..f04d55a26ff1 100644 --- a/yarn-project/ethereum/src/test/start_anvil.test.ts +++ b/yarn-project/ethereum/src/test/start_anvil.test.ts @@ -5,6 +5,15 @@ import { startAnvil } from './start_anvil.js'; describe('start_anvil', () => { it('starts anvil on a free port', async () => { const { anvil, rpcUrl } = await startAnvil(); + + const port = parseInt(new URL(rpcUrl).port); + expect(port).toBeLessThan(65536); + expect(port).toBeGreaterThan(1024); + expect(anvil.port).toEqual(port); + + const host = new URL(rpcUrl).hostname; + expect(anvil.host).toEqual(host); + const publicClient = createPublicClient({ transport: http(rpcUrl) }); const chainId = await publicClient.getChainId(); expect(chainId).toEqual(31337); diff --git a/yarn-project/ethereum/src/test/start_anvil.ts b/yarn-project/ethereum/src/test/start_anvil.ts index b8c287681b31..9c81b2e2832a 100644 --- a/yarn-project/ethereum/src/test/start_anvil.ts +++ b/yarn-project/ethereum/src/test/start_anvil.ts @@ -2,38 +2,47 @@ import { makeBackoff, retry } from '@aztec/foundation/retry'; import { fileURLToPath } from '@aztec/foundation/url'; import { type Anvil, createAnvil } from '@viem/anvil'; -import getPort from 'get-port'; import { dirname, resolve } from 'path'; /** * Ensures there's a running Anvil instance and returns the RPC URL. */ export async function startAnvil(l1BlockTime?: number): Promise<{ anvil: Anvil; rpcUrl: string }> { - let ethereumHostPort: number | undefined; - const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh'); + let port: number | undefined; + // Start anvil. // We go via a wrapper script to ensure if the parent dies, anvil dies. const anvil = await retry( async () => { - ethereumHostPort = await getPort(); const anvil = createAnvil({ anvilBinary, - port: ethereumHostPort, + port: 0, blockTime: l1BlockTime, + stopTimeout: 1000, + }); + + // Listen to the anvil output to get the port. + const removeHandler = anvil.on('message', (message: string) => { + if (port === undefined && message.includes('Listening on')) { + port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)![2]); + } }); await anvil.start(); + removeHandler(); + return anvil; }, 'Start anvil', makeBackoff([5, 5, 5]), ); - if (!ethereumHostPort) { + if (!port) { throw new Error('Failed to start anvil'); } - const rpcUrl = `http://127.0.0.1:${ethereumHostPort}`; - return { anvil, rpcUrl }; + // Monkeypatch the anvil instance to include the actually assigned port + Object.defineProperty(anvil, 'port', { value: port, writable: false }); + return { anvil, rpcUrl: `http://127.0.0.1:${port}` }; }