Skip to content
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: support for deploy time library linking #1505

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/hardhat-zksync-deploy/src/deployer-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types';
import * as zk from 'zksync-ethers';
import * as ethers from 'ethers';
import { AbstractDeployer } from './abstract-deployer';
import { ZkSyncArtifact } from './types';
import { ZkSyncArtifact, ZkSyncOverrides } from './types';
import {
Providers,
createProviders,
Expand Down Expand Up @@ -30,7 +30,7 @@ export class DeployerExtension implements AbstractDeployer {
contractNameOrArtifact: ZkSyncArtifact | string,
constructorArguments: any[] = [],
deploymentType?: zk.types.DeploymentType,
overrides?: ethers.Overrides,
overrides?: ZkSyncOverrides,
additionalFactoryDeps?: ethers.BytesLike[],
): Promise<zk.Contract> {
if (!this.wallet) {
Expand Down
35 changes: 32 additions & 3 deletions packages/hardhat-zksync-deploy/src/deployer-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { HardhatRuntimeEnvironment, HttpNetworkConfig, Network, NetworksConfig }
import { DeploymentType } from 'zksync-ethers/build/types';
import * as zk from 'zksync-ethers';
import * as ethers from 'ethers';
import { ZkSyncArtifact } from './types';
import { TASK_COMPILE_LINK } from '@matterlabs/hardhat-zksync-solc/dist/src/constants';
import { generateFQN } from '@matterlabs/hardhat-zksync-solc/dist/src/utils';
import { ZkSyncArtifact, ZkSyncOverrides } from './types';
import { ZkSyncDeployPluginError } from './errors';
import { isHttpNetworkConfig, isValidEthNetworkURL } from './utils';
import { loadCache, saveCache } from './deployment-saver';
Expand Down Expand Up @@ -64,13 +66,23 @@ export async function deploy(
constructorArguments: any[] = [],
zkWallet: zk.Wallet,
deploymentType: DeploymentType = 'create',
overrides?: ethers.Overrides,
overrides?: ZkSyncOverrides,
additionalFactoryDeps?: ethers.BytesLike[],
): Promise<zk.Contract> {
const linkedBytecode = await linkLibrariesIfNeeded(hre, contractNameOrArtifact, overrides?.libraries);

const artifact: ZkSyncArtifact =
typeof contractNameOrArtifact === 'string'
? await loadArtifact(hre, contractNameOrArtifact)
: contractNameOrArtifact;
: await loadArtifact(
hre,
generateFQN(contractNameOrArtifact.sourceName, contractNameOrArtifact.contractName),
);

if (linkedBytecode) {
artifact.bytecode = linkedBytecode;
artifact.deployedBytecode = linkedBytecode;
}

const baseDeps = await _extractFactoryDeps(hre, artifact);
const additionalDeps = additionalFactoryDeps ? additionalFactoryDeps.map((val) => ethers.hexlify(val)) : [];
Expand Down Expand Up @@ -203,6 +215,23 @@ export async function _extractFactoryDepsRecursive(
return factoryDeps;
}

export async function linkLibrariesIfNeeded(
hre: HardhatRuntimeEnvironment,
contractNameOrArtifact: ZkSyncArtifact | string,
libraries?: { [libraryName: string]: string },
) {
const artifact: ZkSyncArtifact =
typeof contractNameOrArtifact === 'string'
? await loadArtifact(hre, contractNameOrArtifact)
: contractNameOrArtifact;

return await hre.run(TASK_COMPILE_LINK, {
sourceName: artifact.sourceName,
contractName: artifact.contractName,
libraries,
});
}

export function createProviders(
networks: NetworksConfig,
network: Network,
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat-zksync-deploy/src/deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types';
import * as zk from 'zksync-ethers';
import * as ethers from 'ethers';

import { ZkSyncArtifact } from './types';
import { ZkSyncArtifact, ZkSyncOverrides } from './types';
import {
Providers,
createProviders,
Expand Down Expand Up @@ -56,7 +56,7 @@ export class Deployer implements AbstractDeployer {
contractNameOrArtifact: ZkSyncArtifact | string,
constructorArguments: any[] = [],
deploymentType?: zk.types.DeploymentType,
overrides?: ethers.Overrides,
overrides?: ZkSyncOverrides,
additionalFactoryDeps?: ethers.BytesLike[],
): Promise<zk.Contract> {
return await deploy(
Expand Down
5 changes: 5 additions & 0 deletions packages/hardhat-zksync-deploy/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ethers } from 'ethers';
import { Artifact } from 'hardhat/types';

/**
Expand Down Expand Up @@ -42,3 +43,7 @@ export interface ContractFullQualifiedName {
export interface DeployerAccount {
[networkName: string]: number | undefined;
}

export interface ZkSyncOverrides extends ethers.Overrides {
libraries?: { [libraryName: string]: string };
}
4 changes: 3 additions & 1 deletion packages/hardhat-zksync-ethers/src/extension-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export class ZkSyncGenerator implements Generator {
deployContract: (
artifact: ZkSyncArtifact,
constructorArguments: any[],
walletOrSigner?: HardhatZksyncSignerOrWallet,
walletOrSigner?: HardhatZksyncSignerOrWalletOrFactoryOptions,
deploymentType?: DeploymentType,
overrides?: Overrides,
additionalFactoryDeps?: BytesLike[],
) =>
Expand All @@ -66,6 +67,7 @@ export class ZkSyncGenerator implements Generator {
artifact,
constructorArguments,
walletOrSigner,
deploymentType,
overrides,
additionalFactoryDeps,
),
Expand Down
32 changes: 24 additions & 8 deletions packages/hardhat-zksync-ethers/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ import {
ZkSyncArtifact,
} from './types';
import { ZkSyncEthersPluginError } from './errors';
import { getSignerAccounts, getSignerOrWallet, getWalletsFromAccount, isArtifact, isNumber, isString } from './utils';
import {
getSignerAccounts,
getSignerOrWallet,
getWalletsFromAccount,
isArtifact,
isFactoryOptions,
isNumber,
isString,
linkLibrariesIfNeeded,
} from './utils';
import { ZKSOLC_ARTIFACT_FORMAT_VERSION, ZKVYPER_ARTIFACT_FORMAT_VERSION } from './constants';
import { HardhatZksyncSigner } from './signers/hardhat-zksync-signer';

Expand Down Expand Up @@ -129,11 +138,19 @@ If you want to call a contract using ${artifact.contractName} as its interface u
);
}

const libraries: { [libraryName: string]: string } | undefined = isFactoryOptions(walletOrSignerOrOptions)
? walletOrSignerOrOptions.libraries
: undefined;

const linkedBytecode = await linkLibrariesIfNeeded(hre, artifact, libraries);

const walletOrSigner: HardhatZksyncSignerOrWallet | undefined = getSignerOrWallet(walletOrSignerOrOptions);

return getContractFactoryByAbiAndBytecode(
hre,
artifact.abi,
artifact.bytecode,
walletOrSignerOrOptions,
linkedBytecode ?? artifact.bytecode,
walletOrSigner,
deploymentType,
);
}
Expand All @@ -142,11 +159,9 @@ async function getContractFactoryByAbiAndBytecode<A extends any[] = any[], I = C
hre: HardhatRuntimeEnvironment,
abi: any[],
bytecode: ethers.BytesLike,
walletOrSignerOrOptions?: HardhatZksyncSignerOrWalletOrFactoryOptions,
walletOrSigner?: HardhatZksyncSignerOrWallet,
deploymentType?: DeploymentType,
): Promise<ContractFactory<A, I>> {
let walletOrSigner: HardhatZksyncSignerOrWallet | undefined = getSignerOrWallet(walletOrSignerOrOptions);

if (!walletOrSigner) {
walletOrSigner = (await getSigners(hre))[0];
}
Expand Down Expand Up @@ -208,7 +223,8 @@ export async function deployContract(
hre: HardhatRuntimeEnvironment,
artifactOrContract: ZkSyncArtifact | string,
constructorArguments: any[] = [],
walletOrSigner?: HardhatZksyncSignerOrWallet,
walletOrSigner?: HardhatZksyncSignerOrWalletOrFactoryOptions,
deploymentType?: DeploymentType,
overrides?: ethers.Overrides,
additionalFactoryDeps?: ethers.BytesLike[],
): Promise<Contract> {
Expand All @@ -219,7 +235,7 @@ export async function deployContract(
const artifact =
typeof artifactOrContract === 'string' ? await loadArtifact(hre, artifactOrContract) : artifactOrContract;

const factory = await getContractFactoryFromArtifact(hre, artifact, walletOrSigner);
const factory = await getContractFactoryFromArtifact(hre, artifact, walletOrSigner, deploymentType);

const baseDeps = await extractFactoryDeps(hre, artifact);
const additionalDeps = additionalFactoryDeps ? additionalFactoryDeps.map((val) => ethers.hexlify(val)) : [];
Expand Down
10 changes: 6 additions & 4 deletions packages/hardhat-zksync-ethers/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,28 @@ export interface ZkSyncArtifact extends Artifact {
export interface ZkFactoryOptions {
wallet?: Wallet;
signer?: HardhatZksyncSigner;
libraries?: { [libraryName: string]: string };
}

export type HardhatZksyncSignerOrWallet = Wallet | HardhatZksyncSigner;
export type HardhatZksyncSignerOrWalletOrFactoryOptions = HardhatZksyncSignerOrWallet | ZkFactoryOptions;

export type GetContractFactoryArtifactName<A extends any[] = any[], I = Contract> = (
name: string,
walletOrSigner?: HardhatZksyncSignerOrWalletOrFactoryOptions,
walletOrSignerOrOptions?: HardhatZksyncSignerOrWalletOrFactoryOptions,
deploymentType?: DeploymentType,
) => Promise<ContractFactory<A, I>>;

export type GetContractFactoryAbiBytecode<A extends any[] = any[], I = Contract> = (
abi: any[],
bytecode: ethers.BytesLike,
walletOrSigner?: HardhatZksyncSignerOrWalletOrFactoryOptions,
walletOrSigner?: HardhatZksyncSignerOrWallet,
deploymentType?: DeploymentType,
) => Promise<ContractFactory<A, I>>;

export type GetContractFactoryArtifact<A extends any[] = any[], I = Contract> = (
artifact: ZkSyncArtifact,
walletOrSigner?: HardhatZksyncSignerOrWalletOrFactoryOptions,
walletOrSignerOrOptions?: HardhatZksyncSignerOrWalletOrFactoryOptions,
deploymentType?: DeploymentType,
) => Promise<ContractFactory<A, I>>;

Expand Down Expand Up @@ -104,7 +105,8 @@ export interface HardhatZksyncEthersHelpers {
deployContract: (
artifact: ZkSyncArtifact | string,
constructorArguments: any[],
walletOrSigner?: HardhatZksyncSignerOrWallet,
walletOrSigner?: HardhatZksyncSignerOrWalletOrFactoryOptions,
deploymentType?: DeploymentType,
overrides?: ethers.Overrides,
additionalFactoryDeps?: ethers.BytesLike[],
) => Promise<Contract>;
Expand Down
20 changes: 19 additions & 1 deletion packages/hardhat-zksync-ethers/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { Provider, Signer, Wallet } from 'zksync-ethers';
import { ethers } from 'ethers';
import { isAddressEq } from 'zksync-ethers/build/utils';
import { TASK_COMPILE_LINK } from '@matterlabs/hardhat-zksync-solc/dist/src/constants';
import {
HardhatZksyncSignerOrWallet,
HardhatZksyncSignerOrWalletOrFactoryOptions,
Expand All @@ -26,7 +27,7 @@ import { richWallets } from './rich-wallets';
import { ZkSyncEthersPluginError } from './errors';
import { HardhatZksyncEthersProvider } from './hardhat-zksync-provider';
import { HardhatZksyncSigner } from './signers/hardhat-zksync-signer';
import { getWallets } from './helpers';
import { getWallets, loadArtifact } from './helpers';

export function isHardhatNetworkHDAccountsConfig(object: any): object is HardhatNetworkHDAccountsConfig {
return 'mnemonic' in object;
Expand Down Expand Up @@ -262,3 +263,20 @@ export async function isImpersonatedSigner(provider: Provider, address: string):
await provider.send('hardhat_impersonateAccount', [address]);
return true;
}

export async function linkLibrariesIfNeeded(
hre: HardhatRuntimeEnvironment,
contractNameOrArtifact: ZkSyncArtifact | string,
libraries?: { [libraryName: string]: string },
) {
const artifact: ZkSyncArtifact =
typeof contractNameOrArtifact === 'string'
? await loadArtifact(hre, contractNameOrArtifact)
: contractNameOrArtifact;

return await hre.run(TASK_COMPILE_LINK, {
sourceName: artifact.sourceName,
contractName: artifact.contractName,
libraries,
});
}
2 changes: 2 additions & 0 deletions packages/hardhat-zksync-solc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"undici": "^6.18.2",
"debug": "^4.3.5",
"semver": "^7.6.2",
"lodash": "^4.17.21",
"sinon": "^18.0.0",
"sinon-chai": "^3.7.0",
"proper-lockfile": "^4.1.2"
Expand All @@ -56,6 +57,7 @@
"@types/sinon-chai": "^3.2.12",
"@types/debug": "^4.1.12",
"@types/proper-lockfile": "^4.1.4",
"@types/lodash": "^4.14.202",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"@typescript-eslint/parser": "^7.12.0",
"eslint": "^8.56.0",
Expand Down
29 changes: 28 additions & 1 deletion packages/hardhat-zksync-solc/src/compile/binary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { exec } from 'child_process';
import { ZkSolcConfig } from '../types';
import { LinkLibraries, ZkSolcConfig } from '../types';
import { isBreakableCompilerVersion } from '../utils';

export async function compileWithBinary(
Expand Down Expand Up @@ -45,3 +45,30 @@ export async function compileWithBinary(

return JSON.parse(output);
}

export async function linkWithBinary(config: ZkSolcConfig, linkLibraries: LinkLibraries): Promise<any> {
const { compilerPath } = config.settings;

let processCommand = `${compilerPath} --link ${linkLibraries.contractZbinPath}`;
if (linkLibraries.libraries) {
processCommand += ` --libraries ${Object.entries(linkLibraries.libraries)
.map((lib) => `${lib[0]}=${lib[1]}`)
.join(' ')}`;
}
const output: string = await new Promise((resolve, reject) => {
const process = exec(
processCommand,
{
maxBuffer: 1024 * 1024 * 500,
},
(err, stdout, _stderr) => {
if (err !== null) {
return reject(err);
}
resolve(stdout);
},
);
process.stdin!.end();
});
return JSON.parse(output);
}
Loading
Loading