@@ -10,7 +10,8 @@ import { fileURLToPath } from '@aztec/foundation/url';
1010import { bn254 } from '@noble/curves/bn254' ;
1111import type { Abi , Narrow } from 'abitype' ;
1212import { spawn } from 'child_process' ;
13- import { dirname , resolve } from 'path' ;
13+ import { cpSync , existsSync , mkdirSync , mkdtempSync , readdirSync , rmSync } from 'fs' ;
14+ import { dirname , join , resolve } from 'path' ;
1415import readline from 'readline' ;
1516import type { Hex } from 'viem' ;
1617import { foundry , mainnet , sepolia } from 'viem/chains' ;
@@ -107,17 +108,66 @@ export interface ValidatorJson {
107108}
108109
109110/**
110- * Gets the path to the l1-contracts directory.
111+ * Gets the path to the l1-contracts foundry artifacts directory.
112+ * These are copied from l1-contracts to yarn-project/l1-artifacts/l1-contracts
113+ * during build to make yarn-project self-contained.
111114 */
112115export function getL1ContractsPath ( ) : string {
113- // Try to find l1-contracts relative to this file
114116 const currentDir = dirname ( fileURLToPath ( import . meta. url ) ) ;
115-
116- // Go up from yarn-project/ethereum/src to yarn-project, then to repo root, then to l1-contracts
117- const l1ContractsPath = resolve ( currentDir , '..' , '..' , '..' , 'l1-contracts' ) ;
117+ // Go up from yarn-project/ethereum/dest to yarn-project, then to l1-artifacts/l1-contracts
118+ const l1ContractsPath = resolve ( currentDir , '..' , '..' , 'l1-artifacts' , 'l1-contracts' ) ;
118119 return l1ContractsPath ;
119120}
120121
122+ // Cached deployment directory
123+ let preparedDeployDir : string | undefined ;
124+
125+ function cleanupDeployDir ( ) {
126+ if ( preparedDeployDir ) {
127+ try {
128+ rmSync ( preparedDeployDir , { recursive : true , force : true } ) ;
129+ } catch {
130+ // ignore cleanup errors
131+ }
132+ preparedDeployDir = undefined ;
133+ }
134+ }
135+
136+ /**
137+ * Prepares a temp directory for forge deployment.
138+ * Copies all artifacts with preserved timestamps (required for forge cache validity).
139+ * A fresh broadcast/ directory is created for deployment outputs.
140+ */
141+ export function prepareL1ContractsForDeployment ( ) : string {
142+ if ( preparedDeployDir && existsSync ( preparedDeployDir ) ) {
143+ return preparedDeployDir ;
144+ }
145+
146+ const basePath = getL1ContractsPath ( ) ;
147+ const tempDir = mkdtempSync ( join ( dirname ( basePath ) , '.foundry-deploy-' ) ) ;
148+ preparedDeployDir = tempDir ;
149+ process . on ( 'exit' , cleanupDeployDir ) ;
150+
151+ // Copy all dirs with preserved timestamps (required for forge cache validity)
152+ const copyOpts = { recursive : true , preserveTimestamps : true } ;
153+ cpSync ( join ( basePath , 'out' ) , join ( tempDir , 'out' ) , copyOpts ) ;
154+ cpSync ( join ( basePath , 'lib' ) , join ( tempDir , 'lib' ) , copyOpts ) ;
155+ cpSync ( join ( basePath , 'cache' ) , join ( tempDir , 'cache' ) , copyOpts ) ;
156+ cpSync ( join ( basePath , 'src' ) , join ( tempDir , 'src' ) , copyOpts ) ;
157+ cpSync ( join ( basePath , 'script' ) , join ( tempDir , 'script' ) , copyOpts ) ;
158+ cpSync ( join ( basePath , 'generated' ) , join ( tempDir , 'generated' ) , copyOpts ) ;
159+ cpSync ( join ( basePath , 'foundry.toml' ) , join ( tempDir , 'foundry.toml' ) ) ;
160+ cpSync ( join ( basePath , 'foundry.lock' ) , join ( tempDir , 'foundry.lock' ) ) ;
161+ for ( const file of readdirSync ( basePath ) ) {
162+ if ( file . startsWith ( 'solc-' ) ) {
163+ cpSync ( join ( basePath , file ) , join ( tempDir , file ) ) ;
164+ }
165+ }
166+
167+ mkdirSync ( join ( tempDir , 'broadcast' ) ) ;
168+ return tempDir ;
169+ }
170+
121171/**
122172 * Computes the validator data for passing to Solidity.
123173 * Only computes the G2 public key (which requires scalar multiplication on G2, not available in EVM).
@@ -211,7 +261,6 @@ export async function deployAztecL1Contracts(
211261 'Initial validator funding requires minting tokens, which is not possible with an external token.' ,
212262 ) ;
213263 }
214- const currentDir = dirname ( fileURLToPath ( import . meta. url ) ) ;
215264 const chain = createEthereumChain ( [ rpcUrl ] , chainId ) ;
216265
217266 const l1Client = createExtendedL1Client ( [ rpcUrl ] , privateKey , chain . chainInfo ) ;
@@ -240,8 +289,8 @@ export async function deployAztecL1Contracts(
240289 }
241290 }
242291
243- // Relative location of l1-contracts in monorepo or docker image.
244- const l1ContractsPath = resolve ( currentDir , '..' , '..' , '..' , 'l1-contracts' ) ;
292+ // Use foundry-artifacts from l1-artifacts package
293+ const l1ContractsPath = prepareL1ContractsForDeployment ( ) ;
245294
246295 const FORGE_SCRIPT = 'script/deploy/DeployAztecL1Contracts.s.sol' ;
247296 await maybeForgeForceProductionBuild ( l1ContractsPath , FORGE_SCRIPT , chainId ) ;
@@ -513,10 +562,8 @@ export const deployRollupForUpgrade = async (
513562 | 'zkPassportArgs'
514563 > ,
515564) => {
516- const currentDir = dirname ( fileURLToPath ( import . meta. url ) ) ;
517-
518- // Relative location of l1-contracts in monorepo or docker image.
519- const l1ContractsPath = resolve ( currentDir , '..' , '..' , '..' , 'l1-contracts' ) ;
565+ // Use foundry-artifacts from l1-artifacts package
566+ const l1ContractsPath = prepareL1ContractsForDeployment ( ) ;
520567
521568 const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol' ;
522569 await maybeForgeForceProductionBuild ( l1ContractsPath , FORGE_SCRIPT , chainId ) ;
0 commit comments