Skip to content

Commit

Permalink
feat: dynamic zksync artifact import
Browse files Browse the repository at this point in the history
  • Loading branch information
ljankovic-txfusion committed Oct 15, 2024
1 parent 9f98407 commit cb81ec7
Show file tree
Hide file tree
Showing 18 changed files with 193 additions and 102 deletions.
4 changes: 3 additions & 1 deletion solidity/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
node_modules/
cache/
artifacts/
.artifacts/
types/
dist/
coverage/
Expand All @@ -21,5 +21,7 @@ fixtures/
artifacts-zk
cache-zk

core-utils/zksync/artifacts/output

.zksolc-libraries-cache/
typechain-types/
Empty file.
102 changes: 102 additions & 0 deletions solidity/core-utils/zksync/artifacts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { readdirSync } from 'fs';
import path, { join } from 'path';
import { fileURLToPath } from 'url';

/**
* @dev Represents a ZkSync artifact.
*/
export type ZkSyncArtifact = {
contractName: string;
sourceName: string;
abi: any;
bytecode: string;
deployedBytecode: string;
factoryDeps?: Record<string, string>;
};

/**
* @dev A mapping of artifact names to their corresponding ZkSync artifacts.
*/
export type ArtifactMap = {
[key: string]: ZkSyncArtifact; // Key is the artifact name, value is the ZkSyncArtifact
};

// Get the resolved path to the current file
const currentFilePath = fileURLToPath(import.meta.url); // Convert the module URL to a file path
const currentDirectory = path.dirname(currentFilePath);

/**
* @dev Reads artifact files from the specified directory.
* @param directory The directory to read artifact files from.
* @return An array of artifact file names that end with '.js'.
*/
const getArtifactFiles = (directory: string): string[] => {
return readdirSync(directory).filter((file) => file.endsWith('.js')); // Filter for .js files
};

/**
* @dev Exports the list of artifact names without the .js extension.
* @return An array of artifact names without the .js extension.
*/
export const zksyncArtifactNames = getArtifactFiles(
join(currentDirectory, 'output'),
).map((file) => file.replace('.js', ''));

/**
* @dev Checks if a ZkSync artifact exists by its name.
* @param name The name of the artifact to check.
* @return True if the artifact exists, false otherwise.
*/
export const artifactExists = (name: string): boolean => {
return zksyncArtifactNames.includes(`${name}.js`); // Check if the artifact file exists
};

/**
* @dev Loads a ZkSync artifact by its name.
* @param name The name of the artifact to load.
* @return The loaded ZkSyncArtifact or undefined if it cannot be loaded.
*/
const loadZkArtifact = async (
name: string,
): Promise<ZkSyncArtifact | undefined> => {
try {
const artifactModule = await import(
join(currentDirectory, 'output', `${name}.js`)
); // Dynamically import the artifact module
return artifactModule[name]; // Return the artifact from the artifactModule
} catch (error) {
console.error(`Error loading artifact: ${name}`, error);
return undefined;
}
};

/**
* @dev Loads all ZkSync artifacts into a map.
* @return A map of artifact names to their corresponding ZkSync artifacts.
*/
export const loadAllZkArtifacts = async (): Promise<ArtifactMap> => {
const zkSyncArtifactMap: ArtifactMap = {};

// Load all artifacts concurrently
const loadPromises = zksyncArtifactNames.map(async (artifactFileName) => {
const artifact = await loadZkArtifact(artifactFileName);
if (artifact) {
zkSyncArtifactMap[artifactFileName] = artifact;
}
});

await Promise.all(loadPromises);

return zkSyncArtifactMap; // Return the populated artifact map
};

/**
* @dev Retrieves a specific ZkSync artifact by its file name.
* @param name The name of the artifact to retrieve.
* @return The loaded ZkSyncArtifact or undefined if it cannot be loaded.
*/
export const getZkArtifactByName = async (
name: string,
): Promise<ZkSyncArtifact | undefined> => {
return loadZkArtifact(name);
};
6 changes: 3 additions & 3 deletions solidity/exportBuildArtifact.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ cd "$(dirname "$0")"
# Define the artifacts directory
artifactsDir="./artifacts/build-info"
# Define the output file
outputFileJson="./dist/buildArtifact.json"
outputFileJs="./dist/buildArtifact.js"
outputFileTsd="./dist/buildArtifact.d.ts"
outputFileJson="./dist/evm/buildArtifact.json"
outputFileJs="./dist/evm/buildArtifact.js"
outputFileTsd="./dist/evm/buildArtifact.d.ts"

# log that we're in the script
echo 'Finding and processing hardhat build EVM artifact...'
Expand Down
80 changes: 36 additions & 44 deletions solidity/generate-artifact-exports.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,51 @@ import { fileURLToPath } from 'url';
const cwd = process.cwd();

const zksyncArtifacts = glob(cwd, [
`!./artifacts-zk/contracts/**/*.dbg.json`,
`!./artifacts-zk/@openzeppelin/**/*.dbg.json`,
`./artifacts-zk/contracts/**/+([a-zA-Z0-9_]).json`,
`./artifacts-zk/@openzeppelin/**/+([a-zA-Z0-9_]).json`,
`!./artifacts-zk/!(build-info)/**/*.dbg.json`,
`./artifacts-zk/!(build-info)/**/+([a-zA-Z0-9_]).json`,
]);

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Directory containing your JSON files
const outputFile = join(__dirname, 'types/zksync/artifacts/index.ts');
const outputDir = join(__dirname, 'types/zksync/artifacts');
// Directory to store individual artifact files
const srcOutputDir = join(__dirname, 'core-utils/zksync/artifacts/output');

const zkSyncFileNames = new Set();
// Ensure the output directory exists
if (!existsSync(srcOutputDir)) {
mkdirSync(srcOutputDir, { recursive: true });
}

const zkSyncFileNames = new Set();
let zkSyncArtifactMap = {};

// Start building the TypeScript export string
let exportStatements = zksyncArtifacts
.map((file) => {
const fileContent = readFileSync(file, 'utf-8');
const jsonObject = JSON.parse(fileContent);
const contractName = jsonObject.contractName;
let fileName = `${basename(file, '.json')}__artifact`;

if (zkSyncFileNames.has(fileName)) {
return;
}
zkSyncFileNames.add(fileName);

// Add to artifact map
zkSyncArtifactMap[contractName] = fileName;

// Create a TypeScript object export statement
return `export const ${fileName} = ${JSON.stringify(
jsonObject,
null,
2,
)} as const;`;
})
.join('\n\n');

exportStatements += `\n\nexport const zksyncArtifacts : any[] = [\n${Array.from(
zkSyncFileNames,
).join(',\n')}\n] as const;`;

if (!existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}

// Write the index.ts file
writeFileSync(outputFile, exportStatements);
// Process each artifact file
zksyncArtifacts.forEach((file) => {
const fileContent = readFileSync(file, 'utf-8');
const jsonObject = JSON.parse(fileContent);
const contractName = jsonObject.contractName;
let fileName = `${basename(file, '.json')}`;

if (zkSyncFileNames.has(fileName)) {
return;
}
zkSyncFileNames.add(fileName);

// Add to artifact map
zkSyncArtifactMap[contractName] = fileName;

// Create a TypeScript object export statement
const fileContentEx = `export const ${fileName} = ${JSON.stringify(
jsonObject,
null,
2,
)} as const;`;

// Write individual file
const outputFile = join(srcOutputDir, `${fileName}.ts`);
writeFileSync(outputFile, fileContentEx);
});

console.log(
`Generated TypeScript object exports for ${zksyncArtifacts.length} JSON files in configs/`,
`Generated ${zksyncArtifacts.length} individual TypeScript files in ${srcOutputDir}`,
);
2 changes: 1 addition & 1 deletion solidity/hardhat.config.cts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = {
currency: 'USD',
},
typechain: {
outDir: './types',
outDir: './src/evm/types',
target: 'ethers-v5',
alwaysGenerateOverloads: true,
node16Modules: true,
Expand Down
10 changes: 5 additions & 5 deletions solidity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@
},
"type": "module",
"exports": {
".": "./dist/index.js",
"./mailbox": "./dist/contracts/Mailbox.js",
"./buildArtifact.js": "./dist/buildArtifact.js",
".": "./dist/evm/types/index.js",
"./mailbox": "./dist/evm/types/contracts/Mailbox.js",
"./buildArtifact.js": "./dist/evm/buildArtifact.js",
"./buildArtifact-zksync.js": "./dist/zksync/buildArtifact.js",
"./buildArtifact.json": "./dist/buildArtifact.json",
"./buildArtifact.json": "./dist/evm/buildArtifact.json",
"./contracts": "./contracts",
"./artifacts": "./dist/zksync/artifacts/index.js",
"./artifacts/*": "./dist/zksync/artifacts/*.js"
},
"types": "./dist/index.d.ts",
"types": "./dist/evm/types/index.d.ts",
"files": [
"/dist",
"/contracts",
Expand Down
2 changes: 1 addition & 1 deletion solidity/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./types"
"rootDir": "./core-utils"
},
"exclude": ["./test", "hardhat.config.cts", "zk-hardhat.config.cts", "./dist"]
}
2 changes: 1 addition & 1 deletion solidity/zk-hardhat.config.cts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module.exports = {
currency: 'USD',
},
typechain: {
outDir: './types/zksync',
outDir: './src/zksync/types',
target: 'ethers-v5',
alwaysGenerateOverloads: true,
node16Modules: true,
Expand Down
4 changes: 2 additions & 2 deletions typescript/sdk/src/deploy/EvmModuleDeployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { HyperlaneContracts, HyperlaneFactories } from '../contracts/types.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainMap, ChainName } from '../types.js';
import { getArtifactByContractName } from '../utils/zksync.js';
import { getZKArtifactByContractName } from '../utils/zksync.js';

import { isProxy, proxyConstructorArgs } from './proxy.js';
import { ContractVerifier } from './verify/ContractVerifier.js';
Expand Down Expand Up @@ -72,7 +72,7 @@ export class EvmModuleDeployer<Factories extends HyperlaneFactories> {
)})...`,
);

const artifact = getArtifactByContractName(contractName);
const artifact = await getZKArtifactByContractName(contractName);

if (!artifact) {
throw new Error(`No ZKSync artifact found for contract: ${contractName}`);
Expand Down
15 changes: 11 additions & 4 deletions typescript/sdk/src/deploy/HyperlaneDeployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
TimelockController__factory,
TransparentUpgradeableProxy__factory,
} from '@hyperlane-xyz/core';
import { TimelockController__artifact } from '@hyperlane-xyz/core/artifacts';
import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js';
import {
Address,
Expand All @@ -37,7 +36,7 @@ import { InterchainAccount } from '../middleware/account/InterchainAccount.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { MailboxClientConfig } from '../router/types.js';
import { ChainMap, ChainName } from '../types.js';
import { getArtifactByContractName } from '../utils/zksync.js';
import { getZKArtifactByContractName } from '../utils/zksync.js';

import {
UpgradeConfig,
Expand Down Expand Up @@ -422,7 +421,7 @@ export abstract class HyperlaneDeployer<
const { protocol } = this.multiProvider.getChainMetadata(chain);
const isZKSyncChain = protocol === ProtocolType.ZKSync;
const signer = this.multiProvider.getSigner(chain);
const artifact = getArtifactByContractName(contractName);
const artifact = await getZKArtifactByContractName(contractName);

const contract = await this.multiProvider.handleDeploy(
chain,
Expand Down Expand Up @@ -468,6 +467,10 @@ export abstract class HyperlaneDeployer<

let verificationInput: ContractVerificationInput;
if (isZKSyncChain) {
if (!artifact) {
// TODO: ARTIFACT NOT FOUND ERROR
throw Error('Artifact not found');
}
verificationInput = await getContractVerificationInputForZKSync({
name: contractName,
contract,
Expand Down Expand Up @@ -662,6 +665,10 @@ export abstract class HyperlaneDeployer<
chain: ChainName,
timelockConfig: UpgradeConfig['timelock'],
): Promise<TimelockController> {
const TimelockZkArtifact = await getZKArtifactByContractName(
'TimelockController',
);

return this.multiProvider.handleDeploy(
chain,
new TimelockController__factory(),
Expand All @@ -672,7 +679,7 @@ export abstract class HyperlaneDeployer<
[timelockConfig.roles.executor],
ethers.constants.AddressZero,
],
TimelockController__artifact,
TimelockZkArtifact,
);
}

Expand Down
2 changes: 1 addition & 1 deletion typescript/sdk/src/deploy/verify/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ethers, utils } from 'ethers';

import { ZkSyncArtifact } from '@hyperlane-xyz/core/artifacts';
import { Address, eqAddress } from '@hyperlane-xyz/utils';

import { ChainMap, ChainName } from '../../types.js';
import { ZkSyncArtifact } from '../../utils/zksync.js';

import { ContractVerificationInput } from './types.js';

Expand Down
4 changes: 2 additions & 2 deletions typescript/sdk/src/ism/EvmIsmModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
TestIsm__factory,
TrustedRelayerIsm__factory,
} from '@hyperlane-xyz/core';
import { DefaultFallbackRoutingIsm__artifact } from '@hyperlane-xyz/core/artifacts';
import {
Address,
Domain,
Expand Down Expand Up @@ -51,6 +50,7 @@ import { MultiProvider } from '../providers/MultiProvider.js';
import { AnnotatedEV5Transaction } from '../providers/ProviderType.js';
import { ChainName, ChainNameOrId } from '../types.js';
import { findMatchingLogEvents } from '../utils/logUtils.js';
import { getZKArtifactByContractName } from '../utils/zksync.js';

import { EvmIsmReader } from './EvmIsmReader.js';
import { IsmConfigSchema } from './schemas.js';
Expand Down Expand Up @@ -542,7 +542,7 @@ export class EvmIsmModule extends HyperlaneModule<
this.chain,
new DefaultFallbackRoutingIsm__factory(),
[this.args.addresses.mailbox],
DefaultFallbackRoutingIsm__artifact,
await getZKArtifactByContractName('DefaultFallbackRoutingIsm'),
);

// initialize the fallback routing ISM
Expand Down
Loading

0 comments on commit cb81ec7

Please sign in to comment.