-
Notifications
You must be signed in to change notification settings - Fork 431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: zksync support for core solidity package #5290
base: main
Are you sure you want to change the base?
Changes from all commits
dedaa34
4f541e3
f10e542
36e2b00
c6cc7e9
e13e08a
0546e10
2ee37d4
7770bd7
7d6a156
b90d0f7
c33d90f
e3fba6a
a45c50a
576744b
8f7d04d
e16b0e3
5f380e5
8ac3b22
074e7d4
b48be06
ed0f5a9
8090e97
af971af
814bdb0
827fb7c
ae3acc4
19a4cc2
aec3579
9f42411
59165bb
1058872
bf4aa1b
878719b
a4cd60d
cad2dce
3de0c45
be9cc02
1ebf65a
08c9cf3
258d535
db616cc
0ac64e3
0c66a13
2b4dc4f
a1bf8ee
119bb40
bf3ff4a
d84da15
17cf30d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
'@hyperlane-xyz/core': minor | ||
--- | ||
|
||
Add ZKSync support and restructure build artifacts: | ||
|
||
- Add ZKSync compilation support | ||
- Restructure typechain directory location to core-utils/typechain | ||
- Add ZKSync-specific artifact generation and exports | ||
- Update build process to handle both standard and ZKSync artifacts | ||
- Add new exports for ZKSync build artifacts and contract types |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './typechain/index.js'; | ||
export * from './zksync/index.js'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import type { ZKSyncArtifact } from './types.js'; | ||
|
||
// Default empty artifact array when `yarn build:zk` hasn't been run | ||
// This file will be populated with contract artifacts in dist after running the build:zk command | ||
export const zkSyncContractArtifacts: ZKSyncArtifact[] = [] as const; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Default empty artifact array when `yarn build:zk` hasn't been run | ||
// This file will be populated with build artifacts in dist/zksync after running the build:zk command | ||
export const buildArtifact = { | ||
solcLongVersion: '', | ||
zk_version: '', | ||
input: { | ||
language: 'Solidity', | ||
sources: {}, | ||
settings: { | ||
optimizer: { | ||
enabled: false, | ||
runs: 200, | ||
}, | ||
outputSelection: {}, | ||
evmVersion: 'london', | ||
remappings: [], | ||
}, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from './artifacts.js'; | ||
export * from './buildArtifact.js'; | ||
export * from './utils.js'; | ||
export * from './types.js'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export interface ZKSyncArtifact { | ||
_format: string; | ||
contractName: string; | ||
sourceName: string; | ||
abi: any[]; | ||
bytecode: string; | ||
deployedBytecode: string; | ||
linkReferences: Record<string, any>; | ||
deployedLinkReferences: Record<string, any>; | ||
factoryDeps: Record<string, any>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { zkSyncContractArtifacts } from './artifacts.js'; | ||
import { ZKSyncArtifact } from './types.js'; | ||
|
||
/** | ||
* @dev Get a ZkSync artifact by its name. | ||
* @param name The name of the artifact to get. | ||
* @return The loaded ZKSyncArtifact or undefined if it cannot be found. | ||
*/ | ||
export function getZKSyncArtifactByName( | ||
name: string, | ||
): ZKSyncArtifact | undefined { | ||
return zkSyncContractArtifacts.find( | ||
(artifact) => artifact.contractName === name, | ||
); | ||
} | ||
|
||
/** | ||
* @dev Loads all ZkSync artifacts into an array. | ||
* @return An array of ZkSync artifacts. | ||
*/ | ||
export function loadAllZKSyncArtifacts(): ZKSyncArtifact[] { | ||
return zkSyncContractArtifacts; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,11 @@ export default [ | |
...MonorepoDefaults, | ||
{ | ||
ignores: [ | ||
'./test/**/*', | ||
'./dist/**/*', | ||
'**/test/**/*', | ||
'**/dist/**/*', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for some reason test folder was included in lint unit |
||
'**/typechain/**/*', | ||
'.solcover.js', | ||
'generate-artifact-exports.mjs', | ||
], | ||
}, | ||
]; |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This script for generating zksync artifacts only runs in nodejs environment thus 'fs' is used. Exported artifacts have .d.ts and .js extension and are generated directly to dist folder, and have a shape of js object. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like scripts such as this and 'exportBuildArtifacts.sh', 'update_abis.sh', etc... belong in |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import { promises as fs } from 'fs'; | ||
import { basename, dirname, join } from 'path'; | ||
import { glob } from 'typechain'; | ||
import { fileURLToPath } from 'url'; | ||
|
||
const cwd = process.cwd(); | ||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = dirname(__filename); | ||
|
||
const ROOT_OUTPUT_DIR = join(__dirname, 'dist/zksync/'); | ||
const ARTIFACTS_OUTPUT_DIR = join(ROOT_OUTPUT_DIR, 'artifacts'); | ||
|
||
/** | ||
* @notice Templates for TypeScript artifact generation | ||
*/ | ||
const TEMPLATES = { | ||
JS_ARTIFACT: `\ | ||
export const {name} = {artifact}; | ||
`, | ||
|
||
DTS_ARTIFACT: `\ | ||
import type { ZKSyncArtifact } from '../types.js'; | ||
|
||
export declare const {name}: ZKSyncArtifact; | ||
`, | ||
|
||
JS_INDEX: `\ | ||
{imports} | ||
|
||
export const zkSyncContractArtifacts = [ | ||
{exports} | ||
]; | ||
`, | ||
|
||
DTS_INDEX: `\ | ||
import type { ZKSyncArtifact } from './types.js'; | ||
|
||
export declare const zkSyncContractArtifacts: readonly ZKSyncArtifact[]; | ||
`, | ||
}; | ||
|
||
class ArtifactGenerator { | ||
constructor() { | ||
this.processedFiles = new Set(); | ||
} | ||
|
||
/** | ||
* @notice Retrieves paths of all relevant artifact files | ||
* @dev Excludes debug files and build-info directory | ||
* @return {string[]} Array of file paths matching the glob pattern | ||
*/ | ||
getArtifactPaths() { | ||
return glob(cwd, [ | ||
`!./artifacts-zk/!(build-info)/**/*.dbg.json`, | ||
`./artifacts-zk/!(build-info)/**/+([a-zA-Z0-9_]).json`, | ||
]); | ||
} | ||
|
||
/** | ||
* @notice Creates the output directory if it doesn't exist | ||
*/ | ||
async createOutputDirectory() { | ||
await fs.mkdir(ARTIFACTS_OUTPUT_DIR, { recursive: true }); | ||
} | ||
|
||
/** | ||
* @notice Reads and parses a JSON artifact file | ||
* @param filePath Path to the artifact file | ||
* @return {Promise<Object>} Parsed JSON content | ||
*/ | ||
async readArtifactFile(filePath) { | ||
const content = await fs.readFile(filePath, 'utf-8'); | ||
return JSON.parse(content); | ||
} | ||
|
||
/** | ||
* @notice Generates JavaScript content for a contract artifact | ||
*/ | ||
generateJavaScriptContent(name, artifact) { | ||
return TEMPLATES.JS_ARTIFACT.replace('{name}', name).replace( | ||
'{artifact}', | ||
JSON.stringify(artifact, null, 2), | ||
); | ||
} | ||
|
||
/** | ||
* @notice Generates TypeScript declaration content for a contract artifact | ||
*/ | ||
generateDeclarationContent(name) { | ||
return TEMPLATES.DTS_ARTIFACT.replace('{name}', name); | ||
} | ||
|
||
/** | ||
* @notice Generates index file contents | ||
*/ | ||
generateIndexContents(artifactNames) { | ||
const imports = artifactNames | ||
.map((name) => `import { ${name} } from './artifacts/${name}.js';`) | ||
.join('\n'); | ||
const exports = artifactNames.map((name) => ` ${name},`).join('\n'); | ||
|
||
const jsContent = TEMPLATES.JS_INDEX.replace('{imports}', imports).replace( | ||
'{exports}', | ||
exports, | ||
); | ||
|
||
const dtsContent = TEMPLATES.DTS_INDEX.replace( | ||
'{imports}', | ||
imports, | ||
).replace('{exports}', exports); | ||
|
||
return { jsContent, dtsContent }; | ||
} | ||
|
||
/** | ||
* @notice Processes a single artifact file | ||
*/ | ||
async processArtifact(filePath) { | ||
const name = basename(filePath, '.json'); | ||
|
||
if (this.processedFiles.has(name)) { | ||
return; | ||
} | ||
|
||
const artifact = await this.readArtifactFile(filePath); | ||
|
||
// Generate and write .js file | ||
const jsContent = this.generateJavaScriptContent(name, artifact); | ||
await fs.writeFile( | ||
join(ROOT_OUTPUT_DIR, 'artifacts', `${name}.js`), | ||
jsContent, | ||
); | ||
|
||
// Generate and write .d.ts file | ||
const dtsContent = this.generateDeclarationContent(name); | ||
await fs.writeFile( | ||
join(ROOT_OUTPUT_DIR, 'artifacts', `${name}.d.ts`), | ||
dtsContent, | ||
); | ||
|
||
this.processedFiles.add(name); | ||
} | ||
|
||
async generate() { | ||
try { | ||
await this.createOutputDirectory(); | ||
|
||
const artifactPaths = this.getArtifactPaths(); | ||
|
||
for (const filePath of artifactPaths) { | ||
await this.processArtifact(filePath); | ||
} | ||
|
||
const processedNames = Array.from(this.processedFiles); | ||
|
||
// Generate and write index files | ||
const { jsContent, dtsContent } = | ||
this.generateIndexContents(processedNames); | ||
|
||
await fs.writeFile(join(ROOT_OUTPUT_DIR, 'artifacts.js'), jsContent); | ||
await fs.writeFile(join(ROOT_OUTPUT_DIR, 'artifacts.d.ts'), dtsContent); | ||
|
||
console.log( | ||
`Successfully processed ${processedNames.length} zksync artifacts`, | ||
); | ||
} catch (error) { | ||
console.error('Error processing zksync artifacts:', error); | ||
throw error; | ||
} | ||
} | ||
} | ||
|
||
const generator = new ArtifactGenerator(); | ||
generator.generate().catch(console.error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to zksync conditional building. Since zksync functions are a part of core-utils and zksync classes on sdk expect a value, we need to feed them some default values not to break build process. Same applies for buildArtifact.ts