diff --git a/arb-gateway/src/server.ts b/arb-gateway/src/server.ts index 0827f95e..104dde5c 100644 --- a/arb-gateway/src/server.ts +++ b/arb-gateway/src/server.ts @@ -39,8 +39,8 @@ const proofService = new ArbProofService({ l2RollupAddress, cache: new InMemoryBlockCache(), }); - const gateway = new EVMGateway(proofService); + const router = CcipReadRouter({ port, }); diff --git a/bun.lockb b/bun.lockb index a220d1c4..ead956b6 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/evm-gateway/src/utils.ts b/evm-gateway/src/utils.ts index ca57f484..34fae4f4 100644 --- a/evm-gateway/src/utils.ts +++ b/evm-gateway/src/utils.ts @@ -1,5 +1,5 @@ -import { type Request as CFWRequest } from '@cloudflare/workers-types'; -import { type PropsDecoder } from '@ensdomains/server-analytics'; +import type { Request as CFWRequest } from '@cloudflare/workers-types'; +import type { PropsDecoder } from '@ensdomains/server-analytics'; import { encodeAbiParameters, parseAbiParameter, @@ -10,7 +10,7 @@ import { } from 'viem'; import type { AbiParametersToPrimitiveTypes } from 'abitype'; -import { type StateProof } from './EVMProofHelper.js'; +import type { StateProof } from './EVMProofHelper.js'; export interface Router { handle: (request: CFWRequest) => Promise; diff --git a/l1-gateway/package.json b/l1-gateway/package.json index 30fdb82d..0f6457fd 100644 --- a/l1-gateway/package.json +++ b/l1-gateway/package.json @@ -3,6 +3,7 @@ "version": "0.1.0-beta.4", "author": "Nick Johnson", "license": "MIT", + "type": "module", "repository": { "type": "git", "url": "https://github.com/ensdomains/evmgateway.git" @@ -65,10 +66,11 @@ "dependencies": { "@chainlink/ccip-read-server": "^0.2.1", "@commander-js/extra-typings": "^11.0.0", + "@ensdomains/ccip-read-router": "^0.0.5", "@ensdomains/evm-gateway": "0.1.0-beta.4", "@ethereumjs/block": "^5.0.0", "@nomicfoundation/ethereumjs-block": "^5.0.2", "commander": "^11.0.0", "ethers": "^6.7.1" } -} \ No newline at end of file +} diff --git a/l1-gateway/src/L1ProofService.ts b/l1-gateway/src/L1ProofService.ts index 639aa09b..0855bcf5 100644 --- a/l1-gateway/src/L1ProofService.ts +++ b/l1-gateway/src/L1ProofService.ts @@ -1,43 +1,48 @@ -import { - AbiCoder, - encodeRlp as encodeRlp_, - type AddressLike, - type JsonRpcProvider, -} from 'ethers'; - import { EVMProofHelper, convertIntoMerkleTrieProof, type IProofService, } from '@ensdomains/evm-gateway'; -import { Block, type JsonRpcBlock } from '@ethereumjs/block'; -type RlpObject = Uint8Array | Array; -const encodeRlp = encodeRlp_ as (object: RlpObject) => string; +import { + encodeAbiParameters, + parseAbiParameters, + toRlp, + type Address, + type Client, + type Hex, +} from 'viem'; +import { getBlock } from 'viem/actions'; export type L1ProvableBlock = number; +const toIdkHex = (val: bigint): Hex => { + if (val === 0n) return '0x'; + const hexxed = val.toString(16); + return `0x${hexxed.length % 2 === 0 ? hexxed : `0${hexxed}`}`; +}; + /** * The proofService class can be used to calculate proofs for a given target and slot on the Optimism Bedrock network. * It's also capable of proofing long types such as mappings or string by using all included slots in the proof. * */ export class L1ProofService implements IProofService { - private readonly provider: JsonRpcProvider; + private readonly client: Client; private readonly helper: EVMProofHelper; - constructor(provider: JsonRpcProvider) { - this.provider = provider; - this.helper = new EVMProofHelper(provider); + constructor(client: Client) { + this.client = client; + this.helper = new EVMProofHelper(client); } /** * @dev Returns an object representing a block whose state can be proven on L1. */ - async getProvableBlock(): Promise { - const block = await this.provider.getBlock('latest'); + async getProvableBlock(): Promise { + const block = await getBlock(this.client, { blockTag: 'latest' }); if (!block) throw new Error('No block found'); - return block.number - 1; + return Number(block.number) - 1; } /** @@ -47,12 +52,16 @@ export class L1ProofService implements IProofService { * @param slot The slot to fetch. * @returns The value in `slot` of `address` at block `block` */ - getStorageAt( - block: L1ProvableBlock, - address: AddressLike, - slot: bigint - ): Promise { - return this.helper.getStorageAt(block, address, slot); + getStorageAt({ + block, + address, + slot, + }: { + block: L1ProvableBlock; + address: Address; + slot: bigint; + }): Promise { + return this.helper.getStorageAt({ blockNumber: block, address, slot }); } /** @@ -63,24 +72,65 @@ export class L1ProofService implements IProofService { * @returns A proof of the given slots, encoded in a manner that this service's * corresponding decoding library will understand. */ - async getProofs( - blockNo: L1ProvableBlock, - address: AddressLike, - slots: bigint[] - ): Promise { - const proof = await this.helper.getProofs(blockNo, address, slots); - const rpcBlock: JsonRpcBlock = await this.provider.send( - 'eth_getBlockByNumber', - ['0x' + blockNo.toString(16), false] - ); - const block = Block.fromRPC(rpcBlock); - const blockHeader = encodeRlp(block.header.raw()); - return AbiCoder.defaultAbiCoder().encode( + async getProofs({ + block, + address, + slots, + }: { + block: L1ProvableBlock; + address: Address; + slots: bigint[]; + }): Promise { + const proof = await this.helper.getProofs({ + blockNumber: block, + address, + slots, + }); + const comparisonBlock = await getBlock(this.client, { + blockNumber: BigInt(block), + includeTransactions: false, + }); + + if (!comparisonBlock) throw new Error('Block not found'); + + const headerArray = [ + comparisonBlock.parentHash, + comparisonBlock.sha3Uncles, + comparisonBlock.miner, + comparisonBlock.stateRoot, + comparisonBlock.transactionsRoot, + comparisonBlock.receiptsRoot, + comparisonBlock.logsBloom!, + toIdkHex(comparisonBlock.difficulty), + toIdkHex(comparisonBlock.number!), + toIdkHex(comparisonBlock.gasLimit), + toIdkHex(comparisonBlock.gasUsed), // 10 + toIdkHex(comparisonBlock.timestamp), + comparisonBlock.extraData, + comparisonBlock.mixHash, + comparisonBlock.nonce!, + toIdkHex(comparisonBlock.baseFeePerGas!), // 15 + ...(comparisonBlock.withdrawalsRoot + ? [comparisonBlock.withdrawalsRoot] + : (['0x'] as const)), // anvil ??? + ...(typeof comparisonBlock.blobGasUsed === 'bigint' + ? [ + toIdkHex(comparisonBlock.blobGasUsed), + toIdkHex(comparisonBlock.excessBlobGas), + ] + : []), + ]; + + const blockHeader = toRlp(headerArray) as Hex; + return encodeAbiParameters( + parseAbiParameters([ + '(uint256 blockNumber, bytes blockHeader)', + '(bytes stateTrieWitness, bytes[] storageProofs)', + ]), [ - 'tuple(uint256 blockNo, bytes blockHeader)', - 'tuple(bytes stateTrieWitness, bytes[] storageProofs)', - ], - [{ blockNo, blockHeader }, convertIntoMerkleTrieProof(proof)] + { blockNumber: BigInt(block), blockHeader }, + convertIntoMerkleTrieProof(proof), + ] ); } } diff --git a/l1-gateway/src/index.ts b/l1-gateway/src/index.ts index 68e1c08c..7fb04eaa 100644 --- a/l1-gateway/src/index.ts +++ b/l1-gateway/src/index.ts @@ -1,11 +1,15 @@ import { EVMGateway } from '@ensdomains/evm-gateway'; -import { JsonRpcProvider } from 'ethers'; +import { createClient, http } from 'viem'; import { L1ProofService, type L1ProvableBlock } from './L1ProofService.js'; export type L1Gateway = EVMGateway; -export function makeL1Gateway(provider: JsonRpcProvider): L1Gateway { - return new EVMGateway(new L1ProofService(provider)); +export function createL1Gateway(providerUrl: string): L1Gateway { + const client = createClient({ transport: http(providerUrl) }); + + const proofService = new L1ProofService(client); + + return new EVMGateway(proofService); } export { L1ProofService, type L1ProvableBlock }; diff --git a/l1-gateway/src/server.ts b/l1-gateway/src/server.ts index 058bec29..caff51a6 100644 --- a/l1-gateway/src/server.ts +++ b/l1-gateway/src/server.ts @@ -1,7 +1,8 @@ -import { Server } from '@chainlink/ccip-read-server'; import { Command } from '@commander-js/extra-typings'; +import { CcipReadRouter } from '@ensdomains/ccip-read-router'; import { EVMGateway } from '@ensdomains/evm-gateway'; -import { ethers } from 'ethers'; +import { createClient, http } from 'viem'; + import { L1ProofService } from './L1ProofService.js'; const program = new Command() @@ -10,18 +11,13 @@ const program = new Command() program.parse(); -const options = program.opts(); -const provider = new ethers.JsonRpcProvider(options.providerUrl); -const gateway = new EVMGateway(new L1ProofService(provider)); -const server = new Server(); -gateway.add(server); -const app = server.makeApp('/'); +const { port, providerUrl } = program.opts(); + +const client = createClient({ transport: http(providerUrl) }); +const proofService = new L1ProofService(client); +const gateway = new EVMGateway(proofService); -const port = parseInt(options.port); -if (String(port) !== options.port) throw new Error('Invalid port'); +const router = CcipReadRouter({ port }); +gateway.add(router); -(async () => { - app.listen(port, function () { - console.log(`Listening on ${port}`); - }); -})(); +export default router; diff --git a/l1-gateway/src/worker.ts b/l1-gateway/src/worker.ts index 97b7f52c..d4caf972 100644 --- a/l1-gateway/src/worker.ts +++ b/l1-gateway/src/worker.ts @@ -1,25 +1,24 @@ -import { Request as CFWRequest } from '@cloudflare/workers-types'; -import { Server } from '@ensdomains/ccip-read-cf-worker'; -import type { Router } from '@ensdomains/evm-gateway'; +import type { Request as CFWRequest } from '@cloudflare/workers-types'; +import { CcipReadRouter } from '@ensdomains/ccip-read-router'; +import { EVMGateway } from '@ensdomains/evm-gateway'; +import { createClient, http } from 'viem'; +import { L1ProofService } from './L1ProofService.js'; + interface Env { WORKER_PROVIDER_URL: string; } -let app: Router; + async function fetch(request: CFWRequest, env: Env) { - // Loading libraries dynamically as a temp work around. - // Otherwise, deployment thorws "Error: Script startup exceeded CPU time limit." error - if (!app) { - const ethers = await import('ethers'); - const EVMGateway = (await import('@ensdomains/evm-gateway')).EVMGateway; - const L1ProofService = (await import('./L1ProofService.js')).L1ProofService; - const { WORKER_PROVIDER_URL } = env; - const provider = new ethers.JsonRpcProvider(WORKER_PROVIDER_URL); - const gateway = new EVMGateway(new L1ProofService(provider)); - const server = new Server(); - gateway.add(server); - app = server.makeApp('/'); - } - return app.handle(request); + const { WORKER_PROVIDER_URL } = env; + + const client = createClient({ transport: http(WORKER_PROVIDER_URL) }); + const proofService = new L1ProofService(client); + const gateway = new EVMGateway(proofService); + + const router = CcipReadRouter(); + gateway.add(router); + + return router.fetch(request); } export default { diff --git a/l1-verifier/hardhat.config.ts b/l1-verifier/hardhat.config.cts similarity index 79% rename from l1-verifier/hardhat.config.ts rename to l1-verifier/hardhat.config.cts index a9b0a319..76563b8f 100644 --- a/l1-verifier/hardhat.config.ts +++ b/l1-verifier/hardhat.config.cts @@ -1,10 +1,10 @@ -import '@nomicfoundation/hardhat-toolbox'; +import '@nomicfoundation/hardhat-toolbox-viem'; import { HardhatUserConfig } from 'hardhat/config'; const config: HardhatUserConfig = { solidity: '0.8.19', networks: { - ganache: { + anvil: { url: `http://localhost:${parseInt(process.env['RPC_PORT'] || '8545')}`, }, }, diff --git a/l1-verifier/package.json b/l1-verifier/package.json index fb565e31..7af0a851 100644 --- a/l1-verifier/package.json +++ b/l1-verifier/package.json @@ -2,6 +2,7 @@ "name": "@ensdomains/l1-verifier", "license": "MIT", "version": "0.1.0-beta.4", + "type": "module", "repository": { "type": "git", "url": "https://github.com/ensdomains/evmgateway.git" @@ -11,37 +12,26 @@ }, "homepage": "https://github.com/ensdomains/evmgateway/tree/main/l1-verifier", "scripts": { - "build": "echo 'building l1-verifier...' && bun hardhat compile", + "hh": "NODE_OPTIONS='--experimental-loader ts-node/esm/transpile-only' hardhat", + "build": "echo 'building l1-verifier...' && bun hh compile", "test": "node ./scripts/test.js", - "remote_test": "hardhat run scripts/remote.ts", + "remote_test": "hh run scripts/remote.ts", "clean": "rm -fr artifacts cache node_modules typechain-types", "lint": "exit 0" }, "devDependencies": { + "@ensdomains/ccip-read-router": "^0.0.5", "@ensdomains/l1-gateway": "0.1.0-beta.4", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", - "@nomicfoundation/hardhat-ethers": "^3.0.0", - "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomicfoundation/hardhat-toolbox": "^3.0.0", - "@nomicfoundation/hardhat-verify": "^1.0.0", - "@nomiclabs/hardhat-ganache": "^2.0.1", - "@typechain/ethers-v6": "^0.4.0", - "@typechain/hardhat": "^8.0.0", + "@nomicfoundation/hardhat-toolbox-viem": "^3.0.0", "@types/chai": "^4.2.0", "@types/express": "^4.17.18", "@types/mocha": ">=9.1.0", "@types/supertest": "^2.0.14", + "@viem/anvil": "^0.0.10", + "@whatwg-node/server": "^0.9.49", "chai": "^4.2.0", - "ethers": "^6.8.0", - "express": "^4.18.2", - "ganache": "^7.9.1", - "hardhat": "^2.17.4", - "hardhat-gas-reporter": "^1.0.8", - "solidity-bytes-utils": "^0.8.0", - "solidity-coverage": "^0.8.1", - "supertest": "^6.3.3", + "hardhat": "^2.22.9", "ts-node": "^10.9.1", - "typechain": "^8.2.0", "typescript": "^5.2.2" }, "dependencies": { diff --git a/l1-verifier/scripts/test.js b/l1-verifier/scripts/test.js index c7bba803..b69651e6 100644 --- a/l1-verifier/scripts/test.js +++ b/l1-verifier/scripts/test.js @@ -1,45 +1,49 @@ -const { fork } = require('node:child_process'); -const ganache = require('ganache'); -const options = { - logging: { - quiet: true, - }, -}; - -async function main() { - const server = ganache.server(options); - console.log('Starting server'); - const port = await new Promise((resolve, reject) => { - server.listen(0, async (err) => { - console.log(`Listening on port ${server.address().port}`); - if (err) reject(err); - resolve(server.address().port); - }); - }); - - console.log('Starting hardhat'); - const code = await new Promise((resolve) => { - const hh = fork( - '../node_modules/.bin/hardhat', - ['test', '--network', 'ganache'], - { - stdio: 'inherit', - env: { - RPC_PORT: port.toString(), - }, - } - ); - hh.on('close', (code) => resolve(code)); - }); - - console.log('Shutting down'); - server.close(); - process.exit(code); -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main().catch((error) => { - console.error(error); - process.exitCode = 1; +import { CcipReadRouter } from '@ensdomains/ccip-read-router'; +import { EVMGateway } from '@ensdomains/evm-gateway'; +import { L1ProofService } from '@ensdomains/l1-gateway'; +import { createAnvil } from '@viem/anvil'; +import { createServerAdapter } from '@whatwg-node/server'; +import { fork } from 'child_process'; +import { createServer } from 'http'; +import { createClient, http } from 'viem'; + +const SERVER_PORT = 3001; + +const anvil = createAnvil(); + +await anvil.start(); + +const client = createClient({ + transport: http(`http://${anvil.host}:${anvil.port}`), }); +const proofService = new L1ProofService(client); +const gateway = new EVMGateway(proofService); + +const router = CcipReadRouter(); +gateway.add(router); +const ccipReadServer = createServerAdapter(router.fetch); + +const httpServer = createServer(ccipReadServer); +httpServer.listen(SERVER_PORT); + +console.log('Starting hardhat'); +const code = await new Promise((resolve) => { + const hh = fork( + '../node_modules/.bin/hardhat', + ['test', '--network', 'anvil'], + { + stdio: 'inherit', + env: { + NODE_OPTIONS: '--experimental-loader ts-node/esm/transpile-only', + RPC_PORT: anvil.port.toString(), + SERVER_PORT: SERVER_PORT.toString(), + }, + } + ); + hh.on('close', (c) => resolve(c ?? 0)); +}); + +console.log('Shutting down'); +httpServer.close(); +anvil.stop(); +process.exit(code); diff --git a/l1-verifier/test/testL1Verifier.ts b/l1-verifier/test/testL1Verifier.ts index 09cad51b..35d82fba 100644 --- a/l1-verifier/test/testL1Verifier.ts +++ b/l1-verifier/test/testL1Verifier.ts @@ -1,155 +1,112 @@ -import { Server } from '@chainlink/ccip-read-server'; -import { makeL1Gateway } from '@ensdomains/l1-gateway'; -import { HardhatEthersProvider } from '@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider'; -import type { HardhatEthersHelpers } from '@nomicfoundation/hardhat-ethers/types'; import { expect } from 'chai'; -import { - BrowserProvider, - Contract, - FetchRequest, - JsonRpcProvider, - Signer, - ethers as ethersT, -} from 'ethers'; -import { ethers } from 'hardhat'; -import { EthereumProvider } from 'hardhat/types'; -import request from 'supertest'; - -type ethersObj = typeof ethersT & - Omit & { - provider: Omit & { - _hardhatProvider: EthereumProvider; - }; - }; - -declare module 'hardhat/types/runtime' { - const ethers: ethersObj; - interface HardhatRuntimeEnvironment { - ethers: ethersObj; - } -} +import hre from 'hardhat'; -describe('L1Verifier', () => { - let provider: BrowserProvider; - let signer: Signer; - let verifier: Contract; - let target: Contract; - - before(async () => { - // Hack to get a 'real' ethers provider from hardhat. The default `HardhatProvider` - // doesn't support CCIP-read. - provider = new ethers.BrowserProvider(ethers.provider._hardhatProvider); - // provider.on("debug", (x: any) => console.log(JSON.stringify(x, undefined, 2))); - signer = await provider.getSigner(0); - const gateway = makeL1Gateway(provider as unknown as JsonRpcProvider); - const server = new Server(); - gateway.add(server); - const app = server.makeApp('/'); - const getUrl = FetchRequest.createGetUrlFunc(); - ethers.FetchRequest.registerGetUrl(async (req: FetchRequest) => { - if (req.url != 'test:') return getUrl(req); - - const r = request(app).post('/'); - if (req.hasBody()) { - r.set('Content-Type', 'application/json').send( - ethers.toUtf8String(req.body) - ); - } - const response = await r; - return { - statusCode: response.statusCode, - statusMessage: response.ok ? 'OK' : response.statusCode.toString(), - body: ethers.toUtf8Bytes(JSON.stringify(response.body)), - headers: { - 'Content-Type': 'application/json', - }, - }; - }); - const l1VerifierFactory = await ethers.getContractFactory( - 'L1Verifier', - signer - ); - verifier = await l1VerifierFactory.deploy(['test:']); +async function fixture() { + const client = await hre.viem.getPublicClient(); - const testL2Factory = await ethers.getContractFactory('TestL2', signer); - const l2contract = await testL2Factory.deploy(); + const l1Verifier = await hre.viem.deployContract('L1Verifier', [ + [`http://0.0.0.0:${process.env.SERVER_PORT}/{sender}/{data}.json`], + ]); + const l2Contract = await hre.viem.deployContract('TestL2', []); + const target = await hre.viem.deployContract('TestL1', [ + l1Verifier.address, + l2Contract.address, + ]); - const testL1Factory = await ethers.getContractFactory('TestL1', signer); - target = await testL1Factory.deploy( - await verifier.getAddress(), - await l2contract.getAddress() - ); - // Mine an empty block so we have something to prove against - await provider.send('evm_mine', []); - }); + return { client, l1Verifier, target }; +} +describe('L1Verifier', () => { it('simple proofs for fixed values', async () => { - const result = await target.getLatest({ enableCcipRead: true }); - expect(Number(result)).to.equal(42); + const { target } = await fixture(); + + const result = await target.read.getLatest(); + expect(result).to.equal(42n); }); it('simple proofs for dynamic values', async () => { - const result = await target.getName({ enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getName(); expect(result).to.equal('Satoshi'); }); it('nested proofs for dynamic values', async () => { - const result = await target.getHighscorer(42, { enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getHighscorer([42n]); expect(result).to.equal('Hal Finney'); }); it('nested proofs for long dynamic values', async () => { - const result = await target.getHighscorer(1, { enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getHighscorer([1n]); expect(result).to.equal( 'Hubert Blaine Wolfeschlegelsteinhausenbergerdorff Sr.' ); }); it('nested proofs with lookbehind', async () => { - const result = await target.getLatestHighscore({ enableCcipRead: true }); - expect(Number(result)).to.equal(12345); + const { target } = await fixture(); + + const result = await target.read.getLatestHighscore(); + expect(result).to.equal(12345n); }); it('nested proofs with lookbehind for dynamic values', async () => { - const result = await target.getLatestHighscorer({ enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getLatestHighscorer(); expect(result).to.equal('Hal Finney'); }); it('mappings with variable-length keys', async () => { - const result = await target.getNickname('Money Skeleton', { - enableCcipRead: true, - }); + const { target } = await fixture(); + + const result = await target.read.getNickname(['Money Skeleton']); expect(result).to.equal('Vitalik Buterin'); }); it('nested proofs of mappings with variable-length keys', async () => { - const result = await target.getPrimaryNickname({ enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getPrimaryNickname(); expect(result).to.equal('Hal Finney'); }); it('treats uninitialized storage elements as zeroes', async () => { - const result = await target.getZero({ enableCcipRead: true }); - expect(Number(result)).to.equal(0); + const { target } = await fixture(); + + const result = await target.read.getZero(); + expect(result).to.equal(0n); }); it('treats uninitialized dynamic values as empty strings', async () => { - const result = await target.getNickname('Santa', { enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getNickname(['Santa']); expect(result).to.equal(''); }); it('will index on uninitialized values', async () => { - const result = await target.getZeroIndex({ enableCcipRead: true }); - expect(Number(result)).to.equal(1); + const { target } = await fixture(); + + const result = await target.read.getZeroIndex(); + expect(result).to.equal(1n); }); it('will read static value from a struct in a map', async () => { - const result = await target.getStructOffsetValue(1, { enableCcipRead: true }); - expect(Number(result)).to.equal(1337); + const { target } = await fixture(); + + const result = await target.read.getStructOffsetValue([1n]); + expect(result).to.equal(1337n); }); it('will read dynamic value from map in a struct in a map', async () => { - const result = await target.getStructLatestMappedValue('Nick', { enableCcipRead: true }); + const { target } = await fixture(); + + const result = await target.read.getStructLatestMappedValue(['Nick']); expect(result).to.equal('Johnson'); }); - }); diff --git a/l1-verifier/tsconfig.json b/l1-verifier/tsconfig.json index 2e8555d7..cb289869 100644 --- a/l1-verifier/tsconfig.json +++ b/l1-verifier/tsconfig.json @@ -1,14 +1,20 @@ { "extends": "../tsconfig.json", - "include": ["./scripts", "./test"], - "files": ["hardhat.config.ts"], + "include": ["./scripts", "./test", "./artifacts"], + "files": ["hardhat.config.cts"], "compilerOptions": { "esModuleInterop": true, "verbatimModuleSyntax": false, "allowSyntheticDefaultImports": true, - "target": "ES2020", - "module": "CommonJS", - "moduleResolution": "Node", + "target": "ES2022", + "module": "Node16", + "moduleDetection": "force", + "moduleResolution": "NodeNext", "skipLibCheck": true + }, + "ts-node": { + "experimentalResolver": true, + "experimentalSpecifierResolution": "node", + "files": true } }