Skip to content

Commit

Permalink
fix: extend deploy and upgrade of proxy with deployment type and salt…
Browse files Browse the repository at this point in the history
… for ethers-v5 (#1042)

* fix: extend deploy and upgrade of proxy with deployment type and salt

---------

Co-authored-by: Marko Arambasic <[email protected]>
  • Loading branch information
kiriyaga-txfusion and kiriyaga authored May 7, 2024
1 parent 4209c92 commit b758926
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 56 deletions.
97 changes: 92 additions & 5 deletions packages/hardhat-zksync-upgradable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@ 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.
Expand All @@ -56,6 +76,46 @@ 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.

```
const beacon = await hre.zkUpgrades.deployBeacon(deployer.zkWallet, boxContract, {
deploymentType: 'create2',
salt: '0x5273795673417857416686492163276941983664248508133571812215241323'
});
await beacon.deployed();
const box = await hre.zkUpgrades.deployBeaconProxy(deployer.zkWallet, beacon, boxContract, [42], {
deploymentType: 'create2',
salt: '0x6273795673417857416686492163276941983664248508133571812215241323'
});
await box.deployed();
```

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).
Expand All @@ -67,6 +127,16 @@ const myContractV2 = await deployer.loadArtifact('contractV2');
await hre.zkUpgrades.upgradeProxy(deployer.zkWallet, <PROXY_ADDRESS>, 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, <PROXY_ADDRESS>, 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.
Expand All @@ -80,6 +150,16 @@ const myContractV2 = await deployer.loadArtifact('contractV2');
await hre.zkUpgrades.upgradeBeacon(deployer.zkWallet, <BEACON_PROXY_ADDRESS>, 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, <BEACON_PROXY_ADDRESS>, 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)

Expand Down Expand Up @@ -173,22 +253,22 @@ const config: HardhatUserConfig = {

### 🕹 Command list

`yarn hardhat deploy-zksync:proxy --contract-name <contract name or FQN> [<constructor arguments>] [--constructor-args <javascript module name>] [--deployment-type <deployment type>] [--initializer <initialize method>] [--no-compile]`
`yarn hardhat deploy-zksync:proxy --contract-name <contract name or FQN> [<constructor arguments>] [--constructor-args <javascript module name>] [--initializer <initialize method>] [--no-compile] [--deployment-type-impl <deployment type>] [--salt-impl <salt>] [--deployment-type-proxy <deployment type>] [--salt-proxy <salt>]`

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 <contract name or FQN> --proxy-address <proxy address> [--deployment-type <deployment type>] [--no-compile]`
`yarn hardhat upgrade-zksync:proxy --contract-name <contract name or FQN> --proxy-address <proxy address> [--deployment-type <deployment type>] [--salt <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 <proxy address>` argument, e.g. `yarn hardhat upgrade-zksync:proxy --contract-name BoxV2 --proxy-address 0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520`.

`yarn hardhat deploy-zksync:beacon --contract-name <contract name or FQN> [<constructor arguments>] [--constructor-args <javascript module name>] [--deployment-type <deployment type>] [--initializer <initialize method>] [--no-compile]`
`yarn hardhat deploy-zksync:beacon --contract-name <contract name or FQN> [<constructor arguments>] [--constructor-args <javascript module name>] [--initializer <initialize method>] [--deployment-type-impl <deployment type>] [--salt-impl <salt>] [--deployment-type-proxy <deployment type>] [--salt-proxy <salt>] [--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 <contract name or FQN> --beacon-address <beacon address> [--deployment-type <deployment type>] [--no-compile]`
`yarn hardhat upgrade-zksync:beacon --contract-name <contract name or FQN> --beacon-address <beacon address> [--deployment-type <deployment type>] [--salt <salt>] [--no-compile]`

When executed, this command upgrade beacon implementation.
To upgrade a implementation we need to specify beacon address, add `--beacon-address <beacon address>` argument, e.g. `yarn hardhat upgrade-zksync:beacon --contract-name BoxV2 --beacon-address 0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520`.
Expand All @@ -210,7 +290,14 @@ module.exports = [
```
- To provide a initializer method name at deploy tasks, add `--initializer <initializer method>`, 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.

Expand Down
12 changes: 10 additions & 2 deletions packages/hardhat-zksync-upgradable/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -92,21 +95,26 @@ 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);

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);

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);

Expand Down
55 changes: 31 additions & 24 deletions packages/hardhat-zksync-upgradable/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand All @@ -33,23 +36,22 @@ 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.deployed();

const proxy = await hre.zkUpgrades.deployBeaconProxy(
deployer.zkWallet,
beacon.address,
contract,
constructorArguments,
taskArgs.initializer
? {
initializer: taskArgs.initializer,
}
: undefined,
{
deploymentType: taskArgs.deploymentTypeProxy,
salt: taskArgs.saltProxy,
initializer: taskArgs.initializer,
},
);
await proxy.deployed();

Expand All @@ -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<Contract> {
Expand All @@ -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.deployed();

Expand All @@ -102,6 +108,7 @@ export async function upgradeBeacon(
contractName: string;
beaconAddress: string;
deploymentType?: DeploymentType;
salt?: string;
noCompile?: boolean;
},
): Promise<Contract> {
Expand All @@ -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.deployed();

return beaconUpgrade;
Expand All @@ -130,6 +137,7 @@ export async function upgradeProxy(
contractName: string;
proxyAddress: string;
deploymentType?: DeploymentType;
salt?: string;
noCompile?: boolean;
},
): Promise<Contract> {
Expand All @@ -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.deployed();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,16 @@ export function makeDeployBeaconProxy(hre: HardhatRuntimeEnvironment): DeployBea
beaconProxyContract.abi,
beaconProxyContract.bytecode,
wallet,
opts.deploymentType,
);

const proxyDeployment: Required<ProxyDeployment & DeployTransaction> = {
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ export async function deployProxyImpl(
return await deployImpl(hre, deployData, contractFactory, opts);
}

async function deployImpl(
async function deployImpl<TRequiredSeperateForProxy extends boolean | undefined>(
hre: HardhatRuntimeEnvironment,
deployData: DeployData,
factory: zk.ContractFactory,
opts: UpgradeOptions,
opts: UpgradeOptions<TRequiredSeperateForProxy>,
): Promise<any> {
const layout = deployData.layout;

Expand All @@ -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<false>).salt
: (opts as UpgradeOptions).saltImpl,
},
},
],
);

Expand Down
Loading

0 comments on commit b758926

Please sign in to comment.