diff --git a/packages/hardhat-zksync-upgradable/README.md b/packages/hardhat-zksync-upgradable/README.md index 9e7f6262e..e5cd8beee 100644 --- a/packages/hardhat-zksync-upgradable/README.md +++ b/packages/hardhat-zksync-upgradable/README.md @@ -44,6 +44,27 @@ await hre.zkUpgrades.deployProxy(deployer.zkWallet, contract, [initializerFuncti The deployProxy method deploys your implementation contract on zkSync Era, deploys the proxy admin contract, and finally, deploys the transparent proxy. +Additionaly, in the options section optionaly include the folowing arguments to configure the deployment of the proxy and implementation with different deployment types and salts: + + - `deploymentTypeImpl` + - `saltImpl` + - `deploymentTypeProxy` + - `saltProxy` + +``` +await hre.zkUpgrades.deployProxy(deployer.zkWallet, contract, [initializerFunctionArguments], + { initializer: "initialize", + saltImpl: "0x4273795673417857416686492163276941983664248508133571812215241323", + deploymentTypeImpl: "create2", + saltProxy: "0x5273795673417857416686492163276941983664248508133571812215241323", + deploymentTypeProxy: "create2" + } +); +``` + +Permissible values for the deployment type include `create`, `create2`, `createAccount`, and `create2Account`. If this parameter is omitted, the default value will be `create`. +If the salt parameters are ommited, the default value will be `0x0000000000000000000000000000000000000000000000000000000000000000`. + - **Deploying UUPS proxies** The UUPS proxy pattern is similar to the transparent proxy pattern, except that the upgrade is triggered via the logic contract instead of from the proxy contract. @@ -56,6 +77,48 @@ Beacon proxies enable a more advanced upgrade pattern, where multiple implementa Start by creating a Deployer for the zkSync Era network and load the Box artifact: +``` +// mnemonic for local node rich wallet +const testMnemonic = "stuff slice staff easily soup parent arm payment cotton trade scatter struggle"; +const zkWallet = Wallet.fromMnemonic(testMnemonic); + +const deployer = new Deployer(hre, zkWallet); + +const contractName = "Box"; +const boxContract = await deployer.loadArtifact(contractName); +``` + +Deploy the beacon contract using `deployBeacon` method from the `zkUpgrades`: + +``` +await hre.zkUpgrades.deployBeacon(deployer.zkWallet, boxContract); +``` + +Use the `deployBeaconProxy` method which receives the zkSync Era wallet, beacon contract, and the implementation (Box) contract with its arguments. + +``` +const box = await hre.zkUpgrades.deployBeaconProxy(deployer.zkWallet, beacon, boxContract, [42]); + +``` + +Additionaly, in the options section optionaly include the `deploymentType` and `salt` arguments to configure deployment type and salt. + +``` +await hre.zkUpgrades.deployBeacon(deployer.zkWallet, boxContract, { + deploymentType: 'create2', + salt: '0x5273795673417857416686492163276941983664248508133571812215241323' +}); + +const box = await hre.zkUpgrades.deployBeaconProxy(deployer.zkWallet, beacon, boxContract, [42], { + deploymentType: 'create2', + salt: '0x6273795673417857416686492163276941983664248508133571812215241323' +}); + +``` + +Permissible values for the deployment type include `create`, `create2`, `createAccount`, and `create2Account`. If this parameter is omitted, the default value will be `create`. +If the salt parameters are ommited, the default value will be `0x0000000000000000000000000000000000000000000000000000000000000000`. + - **Upgrading proxies** In order for a smart contract implementation to be upgradable, it has to follow specific [rules](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable). @@ -67,6 +130,16 @@ const myContractV2 = await deployer.loadArtifact('contractV2'); await hre.zkUpgrades.upgradeProxy(deployer.zkWallet, , myContractV2); ``` +Optionaly in the other options section include `deploymentType` and `salt` to configure deployment type and salt for deploy of the new implementation. + + ``` +const myContractV2 = await deployer.loadArtifact('contractV2'); +await hre.zkUpgrades.upgradeProxy(deployer.zkWallet, , myContractV2, { + deploymentType: 'create2', + salt: '0x6273795673417857416686492163276941983664248508133571812215241323' +}); +``` + - **Upgrade UUPS proxy** Similar to the deployment script, there are no modifications needed to upgrade the implementation of the UUPS contract, compared to upgrading the transparent upgradable contract. @@ -80,6 +153,16 @@ const myContractV2 = await deployer.loadArtifact('contractV2'); await hre.zkUpgrades.upgradeBeacon(deployer.zkWallet, , myContractV2); ``` +Optionaly in the other options section include `deploymentType` and `salt` to configure deployment type and salt for deploy of the new implementation. + + ``` +const myContractV2 = await deployer.loadArtifact('contractV2'); +await hre.zkUpgrades.upgradeBeacon(deployer.zkWallet, , myContractV2 { + deploymentType: 'create2', + salt: '0x6273795673417857416686492163276941983664248508133571812215241323' +}); +``` + The hardhat-zksync-upgradable plugin supports proxy verification, which means you can verify all the contracts deployed during the proxy deployment with a single verify command. Check how to verify on this [link](https://era.zksync.io/docs/tools/hardhat/hardhat-zksync-upgradable.html#proxy-verification) @@ -173,22 +256,22 @@ const config: HardhatUserConfig = { ### 🕹 Command list -`yarn hardhat deploy-zksync:proxy --contract-name [] [--constructor-args ] [--deployment-type ] [--initializer ] [--no-compile]` +`yarn hardhat deploy-zksync:proxy --contract-name [] [--constructor-args ] [--initializer ] [--no-compile] [--deployment-type-impl ] [--salt-impl ] [--deployment-type-proxy ] [--salt-proxy ]` When executed, this command will automatically determine whether the deployment is for a Transparent or UUPS proxy. If the Transparent proxy is chosen, it will deploy implementation, admin, and proxy. If the UUPS proxy is chosen, it will deploy implementation and proxy. -`yarn hardhat upgrade-zksync:proxy --contract-name --proxy-address [--deployment-type ] [--no-compile]` +`yarn hardhat upgrade-zksync:proxy --contract-name --proxy-address [--deployment-type ] [--salt ] [--no-compile]` When executed, this command upgrade UUPS or Transparent implementation. To upgrade a implementation we need to specify proxy address, add `--proxy-address ` argument, e.g. `yarn hardhat upgrade-zksync:proxy --contract-name BoxV2 --proxy-address 0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520`. -`yarn hardhat deploy-zksync:beacon --contract-name [] [--constructor-args ] [--deployment-type ] [--initializer ] [--no-compile]` +`yarn hardhat deploy-zksync:beacon --contract-name [] [--constructor-args ] [--initializer ] [--deployment-type-impl ] [--salt-impl ] [--deployment-type-proxy ] [--salt-proxy ] [--no-compile]` When executed, this command deploys the provided implementation, beacon and proxy on the specified network, using the provided contract constructor arguments. -`yarn hardhat upgrade-zksync:beacon --contract-name --beacon-address [--deployment-type ] [--no-compile]` +`yarn hardhat upgrade-zksync:beacon --contract-name --beacon-address [--deployment-type ] [--salt ] [--no-compile]` When executed, this command upgrade beacon implementation. To upgrade a implementation we need to specify beacon address, add `--beacon-address ` argument, e.g. `yarn hardhat upgrade-zksync:beacon --contract-name BoxV2 --beacon-address 0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520`. @@ -210,7 +293,14 @@ module.exports = [ ``` - To provide a initializer method name at deploy tasks, add `--initializer `, e.g. `hardhat deploy-zksync:proxy --contract-name Contract --initializer store`. If this parameter is omitted, the default value will be `initialize`. - To allows the task to skip the compilation process, add `--no-compile` argument, e.g. `hardhat deploy-zksync:beacon --contract-name Contract --no-compile`. -- To allows the task to specify which deployer smart contract function will be called, add `--deployment-type` argument. Permissible values for this parameter include `create`, `create2`, `createAccount`, and `create2Account`. If this parameter is omitted, the default value will be `create`, e.g. `hardhat deploy-zksync:beacon --contract-name Greeter 'Hello' --deployment-type create2`. +- To allows the task to specify which deployer smart contract function will be called for implementation, add `--deployment-type-impl` argument, e.g. `hardhat deploy-zksync:beacon --contract-name Greeter 'Hello' --deployment-type-impl create2`. +- To allows the task to specify which deployer smart contract function will be called for proxy, add `--deployment-type-proxy` argument, e.g. `hardhat deploy-zksync:beacon --contract-name Greeter 'Hello' --deployment-type-proxy create2`. +- To specify which salt will be used in deployment of the implementation, add `--salt-impl` argument, e.g. `hardhat deploy-zksync:beacon --contract-name Greeter 'Hello' --salt-impl 0x42737956734178574166864921632769419836642485081335718122152413290` +- To specify which salt will be used in deployment of the proxy, add `--salt-proxy` argument, e.g. `hardhat deploy-zksync:beacon --contract-name Greeter 'Hello' --salt-proxy 0x42737956734178574166864921632769419836642485081335718122152413290` +- When utilizing the `upgrade-zksync:beacon` or `upgrade-zksync:proxy` tasks, specify the deployment type and salt using the `--deployment-type` and `--salt` arguments respectively. + +Permissible values for the deployment type include `create`, `create2`, `createAccount`, and `create2Account`. If this parameter is omitted, the default value will be `create`. +If the salt parameters are ommited, the default value will be `0x0000000000000000000000000000000000000000000000000000000000000000`. The account used for deployment will be the one specified by the `deployerAccount` configuration within the `hardhat.config.ts` file. If no such configuration is present, the account with index `0` will be used. diff --git a/packages/hardhat-zksync-upgradable/src/index.ts b/packages/hardhat-zksync-upgradable/src/index.ts index 2523bda30..c10c4fab4 100644 --- a/packages/hardhat-zksync-upgradable/src/index.ts +++ b/packages/hardhat-zksync-upgradable/src/index.ts @@ -74,7 +74,10 @@ task(TASK_DEPLOY_ZKSYNC_BEACON, 'Runs the beaccon deploy for zkSync network') types.inputFile, ) .addOptionalParam('initializer', 'Initializer function name', undefined) - .addOptionalParam('deploymentType', 'Type of deployment', undefined) + .addOptionalParam('deploymentTypeImpl', 'Type of deployment for implementation', undefined) + .addOptionalParam('deploymentTypeProxy', 'Type of deployment for proxy', undefined) + .addOptionalParam('saltImpl', 'Salt for implementation deployment', undefined) + .addOptionalParam('saltProxy', 'Salt for proxy deployment', undefined) .addFlag('noCompile', 'No compile flag') .setAction(deployZkSyncBeacon); @@ -92,7 +95,10 @@ task(TASK_DEPLOY_ZKSYNC_PROXY, 'Deploy proxy for zkSync network') types.inputFile, ) .addOptionalParam('initializer', 'Initializer function name', undefined) - .addOptionalParam('deploymentType', 'Type of deployment', undefined) + .addOptionalParam('deploymentTypeImpl', 'Type of deployment for implementation', undefined) + .addOptionalParam('deploymentTypeProxy', 'Type of deployment for proxy', undefined) + .addOptionalParam('saltImpl', 'Salt for implementation deployment', undefined) + .addOptionalParam('saltProxy', 'Salt for proxy deployment', undefined) .addFlag('noCompile', 'No compile flag') .setAction(deployZkSyncProxy); @@ -100,6 +106,7 @@ task(TASK_UPGRADE_ZKSYNC_BEACON, 'Runs the beacon upgrade for zkSync network') .addParam('contractName', 'A contract name or a FQN', '') .addParam('beaconAddress', 'Beacon address of the deployed contract', '') .addOptionalParam('deploymentType', 'Type of deployment', undefined) + .addOptionalParam('salt', 'Salt for deployment', undefined) .addFlag('noCompile', 'No compile flag') .setAction(upgradeZkSyncBeacon); @@ -107,6 +114,7 @@ task(TASK_UPGRADE_ZKSYNC_PROXY, 'Runs the proxy upgrade for zkSync network') .addParam('contractName', 'A contract name or a FQN', '') .addParam('proxyAddress', 'Proxy address of the deployed contract', '') .addOptionalParam('deploymentType', 'Type of deployment', undefined) + .addOptionalParam('salt', 'Salt for deployment', undefined) .addFlag('noCompile', 'No compile flag') .setAction(upgradeZkSyncProxy); diff --git a/packages/hardhat-zksync-upgradable/src/plugin.ts b/packages/hardhat-zksync-upgradable/src/plugin.ts index 2cb4937e1..cdd5507e5 100644 --- a/packages/hardhat-zksync-upgradable/src/plugin.ts +++ b/packages/hardhat-zksync-upgradable/src/plugin.ts @@ -13,7 +13,10 @@ export async function deployBeacon( constructorArgsParams: any[]; constructorArgs?: string; initializer?: string; - deploymentType?: DeploymentType; + deploymentTypeImpl?: DeploymentType; + deploymentTypeProxy?: DeploymentType; + saltImpl?: string; + saltProxy?: string; noCompile?: boolean; }, ): Promise<{ @@ -33,11 +36,10 @@ export async function deployBeacon( const deployer = new Deployer(hre, wallet); const contract = await deployer.loadArtifact(taskArgs.contractName); - const beacon = await hre.zkUpgrades.deployBeacon( - wallet, - contract, - taskArgs.deploymentType ? { deploymentType: taskArgs.deploymentType } : undefined, - ); + const beacon = await hre.zkUpgrades.deployBeacon(wallet, contract, { + deploymentType: taskArgs.deploymentTypeImpl, + salt: taskArgs.saltImpl, + }); await beacon.waitForDeployment(); const proxy = await hre.zkUpgrades.deployBeaconProxy( @@ -45,11 +47,11 @@ export async function deployBeacon( await beacon.getAddress(), contract, constructorArguments, - taskArgs.initializer - ? { - initializer: taskArgs.initializer, - } - : undefined, + { + deploymentType: taskArgs.deploymentTypeProxy, + salt: taskArgs.saltProxy, + initializer: taskArgs.initializer, + }, ); await proxy.waitForDeployment(); @@ -66,7 +68,10 @@ export async function deployProxy( constructorArgsParams: any[]; constructorArgs?: string; initializer?: string; - deploymentType?: DeploymentType; + deploymentTypeImpl?: DeploymentType; + deploymentTypeProxy?: DeploymentType; + saltImpl?: string; + saltProxy?: string; noCompile?: boolean; }, ): Promise { @@ -84,12 +89,13 @@ export async function deployProxy( const contract = await deployer.loadArtifact(taskArgs.contractName); - const opts = { - deploymentType: taskArgs.deploymentType, + const proxy = await hre.zkUpgrades.deployProxy(wallet, contract, constructorArguments, { + deploymentTypeImpl: taskArgs.deploymentTypeImpl, + saltImpl: taskArgs.saltImpl, + deploymentTypeProxy: taskArgs.deploymentTypeProxy, + saltProxy: taskArgs.saltProxy, initializer: taskArgs.initializer, - }; - - const proxy = await hre.zkUpgrades.deployProxy(wallet, contract, constructorArguments, opts); + }); await proxy.waitForDeployment(); @@ -102,6 +108,7 @@ export async function upgradeBeacon( contractName: string; beaconAddress: string; deploymentType?: DeploymentType; + salt?: string; noCompile?: boolean; }, ): Promise { @@ -114,11 +121,11 @@ export async function upgradeBeacon( const contractV2 = await deployer.loadArtifact(taskArgs.contractName); - const opts = { + const beaconUpgrade = await hre.zkUpgrades.upgradeBeacon(wallet, taskArgs.beaconAddress, contractV2, { deploymentType: taskArgs.deploymentType, - }; + salt: taskArgs.salt, + }); - const beaconUpgrade = await hre.zkUpgrades.upgradeBeacon(wallet, taskArgs.beaconAddress, contractV2, opts); await beaconUpgrade.waitForDeployment(); return beaconUpgrade; @@ -130,6 +137,7 @@ export async function upgradeProxy( contractName: string; proxyAddress: string; deploymentType?: DeploymentType; + salt?: string; noCompile?: boolean; }, ): Promise { @@ -142,11 +150,10 @@ export async function upgradeProxy( const contractV2 = await deployer.loadArtifact(taskArgs.contractName); - const opts = { + const proxyUpgrade = await hre.zkUpgrades.upgradeProxy(wallet, taskArgs.proxyAddress, contractV2, { deploymentType: taskArgs.deploymentType, - }; - - const proxyUpgrade = await hre.zkUpgrades.upgradeProxy(wallet, taskArgs.proxyAddress, contractV2, opts); + salt: taskArgs.salt, + }); await proxyUpgrade.waitForDeployment(); diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts index fa50c03be..d9a2be1c9 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-beacon-proxy.ts @@ -94,12 +94,18 @@ export function makeDeployBeaconProxy(hre: HardhatRuntimeEnvironment): DeployBea beaconProxyContract.abi, beaconProxyContract.bytecode, wallet, + opts.deploymentType, ); const proxyDeployment: Required = { kind: opts.kind, - ...(await deploy(beaconProxyFactory, beaconAddress, data)), + ...(await deploy(beaconProxyFactory, beaconAddress, data, { + customData: { + salt: opts.salt, + }, + })), }; + if (!quiet) { console.info(chalk.green('Beacon proxy deployed at: ', proxyDeployment.address)); } diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts index 9d0e50679..c06184d39 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-impl.ts @@ -63,11 +63,11 @@ export async function deployProxyImpl( return await deployImpl(hre, deployData, contractFactory, opts); } -async function deployImpl( +async function deployImpl( hre: HardhatRuntimeEnvironment, deployData: DeployData, factory: zk.ContractFactory, - opts: UpgradeOptions, + opts: UpgradeOptions, ): Promise { const layout = deployData.layout; @@ -84,7 +84,15 @@ async function deployImpl( factory, ...[ ...deployData.fullOpts.constructorArgs, - { customData: { factoryDeps: deployData.fullOpts.factoryDeps } }, + { + customData: { + factoryDeps: deployData.fullOpts.factoryDeps, + salt: + 'salt' in opts + ? (opts as UpgradeOptions).salt + : (opts as UpgradeOptions).saltImpl, + }, + }, ], ); return deployed; diff --git a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts index 082abfd24..f4ae5db98 100644 --- a/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts +++ b/packages/hardhat-zksync-upgradable/src/proxy-deployment/deploy-proxy.ts @@ -44,7 +44,7 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction artifact.abi, artifact.bytecode, wallet, - opts.deploymentType, + opts.deploymentTypeImpl, ); const { impl, kind } = await deployProxyImpl(hre, factory, opts); @@ -54,6 +54,12 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction const data = getInitializerData(factory.interface, args, opts.initializer); + const customDataProxy = { + customData: { + salt: opts.saltProxy, + }, + }; + if (kind === 'uups') { if (await manifest.getAdmin()) { if (!quiet) { @@ -78,8 +84,13 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction ); assert(ERC1967ProxyPath, 'ERC1967Proxy artifact not found'); const proxyContract = await import(ERC1967ProxyPath); - const proxyFactory = new zk.ContractFactory(proxyContract.abi, proxyContract.bytecode, wallet); - proxyDeployment = { kind, ...(await deploy(proxyFactory, impl, data)) }; + const proxyFactory = new zk.ContractFactory( + proxyContract.abi, + proxyContract.bytecode, + wallet, + opts.deploymentTypeProxy, + ); + proxyDeployment = { kind, ...(await deploy(proxyFactory, impl, data, customDataProxy)) }; if (!quiet) { console.info(chalk.green(`UUPS proxy was deployed to ${proxyDeployment.address}`)); @@ -97,8 +108,13 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment): DeployFunction assert(TUPPath, 'TUP artifact not found'); const TUPContract = await import(TUPPath); - const TUPFactory = new zk.ContractFactory(TUPContract.abi, TUPContract.bytecode, wallet); - proxyDeployment = { kind, ...(await deploy(TUPFactory, impl, adminAddress, data)) }; + const TUPFactory = new zk.ContractFactory( + TUPContract.abi, + TUPContract.bytecode, + wallet, + opts.deploymentTypeProxy, + ); + proxyDeployment = { kind, ...(await deploy(TUPFactory, impl, adminAddress, data, customDataProxy)) }; if (!quiet) { console.info(chalk.green(`Transparent proxy was deployed to ${proxyDeployment.address}`)); diff --git a/packages/hardhat-zksync-upgradable/src/utils/options.ts b/packages/hardhat-zksync-upgradable/src/utils/options.ts index 4855ab3d1..77b3f83ca 100644 --- a/packages/hardhat-zksync-upgradable/src/utils/options.ts +++ b/packages/hardhat-zksync-upgradable/src/utils/options.ts @@ -10,18 +10,34 @@ import { import { DeploymentType } from 'zksync-ethers/build/src/types'; import { LOCAL_SETUP_ZKSYNC_NETWORK } from '../constants'; -export type StandaloneOptions = StandaloneValidationOptions & - DeployOpts & { - constructorArgs?: unknown[]; - useDeployedImplementation?: boolean; - provider?: any; - factoryDeps?: string[]; - deploymentType?: DeploymentType; - }; +export type StandaloneOptions = + StandaloneValidationOptions & + DeployOpts & { + constructorArgs?: unknown[]; + useDeployedImplementation?: boolean; + provider?: any; + factoryDeps?: string[]; + } & DeploymentTypesOptions; -export type UpgradeOptions = ValidationOptions & StandaloneOptions; +export type DeploymentTypesOptions = + TRequiredSeperateForProxy extends true | undefined + ? { + deploymentTypeImpl?: DeploymentType; + deploymentTypeProxy?: DeploymentType; + saltImpl?: string; + saltProxy?: string; + } + : { + deploymentType?: DeploymentType; + salt?: string; + }; -export function withDefaults(opts: UpgradeOptions = {}): Required { +export type UpgradeOptions = + ValidationOptions & StandaloneOptions; + +export function withDefaults( + opts: UpgradeOptions = {}, +): Required> { return { constructorArgs: opts.constructorArgs ?? [], timeout: opts.timeout ?? 60e3, @@ -29,22 +45,21 @@ export function withDefaults(opts: UpgradeOptions = {}): Required>; } interface Initializer { initializer?: string | false; } -export type DeployBeaconProxyOptions = ProxyKindOption & Initializer; -export type DeployBeaconOptions = StandaloneOptions; +export type DeployBeaconProxyOptions = ProxyKindOption & Initializer & DeploymentTypesOptions; +export type DeployBeaconOptions = StandaloneOptions; export type DeployImplementationOptions = StandaloneOptions; export type DeployProxyAdminOptions = DeployOpts; export type DeployProxyOptions = StandaloneOptions & Initializer; -export type UpgradeBeaconOptions = UpgradeOptions; -export type UpgradeProxyOptions = UpgradeOptions & { +export type UpgradeBeaconOptions = UpgradeOptions; +export type UpgradeProxyOptions = UpgradeOptions & { call?: { fn: string; args?: unknown[] } | string; }; export type ValidateImplementationOptions = StandaloneValidationOptions;