Skip to content

Commit

Permalink
feat: bump openzeppelin contracts to v5 (#1463)
Browse files Browse the repository at this point in the history
* feat: refactor upgradable interface

* chore: apply lint

* fix: add additional example and remove unnessecary file

* fix: refactor code, adjust params and set proper version of that oz hardhat-upgradable plugin

* fix: add defender v1 and redesign code

* fix: compile missing contracts before verify

* fix: update check for contracts update

* fix: add paymaster params for proxy and implementation deployment

* fix: add paymaster params for admin and add other custom data

* fix: add verify for non zksync networks

* fix: change custom data for proxy admin

* fix: change custom data for proxy admin

* fix: change paymaster params to PaymasterParams from zksync-ethers

* feat: bump openzeppelin contracts to v5

* fix: revert oz contracts for basic example and update readme file

* fix: revert manifest and storage layout

* fix: remove unsafeAllow for uups proxies

* chore: remove only from tests

* chore: revert a mnemonic

* chore: revert a mnemonic

---------

Co-authored-by: nikola-bozin-txfusion <[email protected]>
Co-authored-by: Marko Arambasic <[email protected]>
  • Loading branch information
3 people authored Oct 1, 2024
1 parent b4c111b commit ebb668b
Show file tree
Hide file tree
Showing 44 changed files with 519 additions and 974 deletions.
4 changes: 2 additions & 2 deletions examples/basic-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"ethers": "^6.12.2",
"zksync-ethers": "^6.11.2",
"@matterlabs/zksync-contracts": "^0.6.1",
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts-upgradeable": "^4.9.2"
"@openzeppelin/contracts": "^4.9.6",
"@openzeppelin/contracts-upgradeable": "^4.9.6"
},
"prettier": {
"tabWidth": 4,
Expand Down
4 changes: 2 additions & 2 deletions examples/node-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"ethers": "^6.12.2",
"zksync-ethers": "^6.11.2",
"@matterlabs/zksync-contracts": "^0.6.1",
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts-upgradeable": "^4.9.2"
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2"
},
"prettier": {
"tabWidth": 4,
Expand Down
4 changes: 2 additions & 2 deletions examples/noninline-libraries-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"ethers": "^6.12.2",
"zksync-ethers": "^6.11.2",
"@matterlabs/zksync-contracts": "^0.6.1",
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts-upgradeable": "^4.9.2"
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2"
},
"prettier": {
"tabWidth": 4,
Expand Down
2 changes: 1 addition & 1 deletion examples/upgradable-example-l1/contracts/BoxUups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract BoxUups is Initializable, UUPSUpgradeable, OwnableUpgradeable {

function initialize(uint256 initValue) public initializer {
value = initValue;
__Ownable_init();
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
}

Expand Down
5 changes: 3 additions & 2 deletions examples/upgradable-example-l1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"clean": "rimraf dist"
},
"devDependencies": {
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts": "^5.0.2",
"@types/node": "^18.11.17",
"@typescript-eslint/eslint-plugin": "6.13.1",
"@typescript-eslint/parser": "6.13.1",
Expand All @@ -35,7 +35,8 @@
"@matterlabs/hardhat-zksync-solc": "workspace:^",
"@matterlabs/hardhat-zksync-upgradable": "workspace:^",
"@matterlabs/hardhat-zksync-verify": "workspace:^",
"@openzeppelin/contracts-upgradeable": "^4.9.2",
"@nomicfoundation/hardhat-ethers": "3.0.6",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"chalk": "^4.1.2",
"ethers": "^6.12.2",
"hardhat": "^2.22.5",
Expand Down
2 changes: 1 addition & 1 deletion examples/upgradable-example-l1/scripts/deploy-box-uups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as hre from 'hardhat';

async function main() {
const Box = await hre.ethers.getContractFactory("BoxUups");
const box = await hre.upgrades.deployProxy(Box,[42],{ initializer: 'initialize'});
const box = await hre.upgrades.deployProxy(Box,[42],{ initializer: 'initialize' });
await box.waitForDeployment()
console.info("Box deployed address: " + await box.getAddress())
}
Expand Down
2 changes: 1 addition & 1 deletion examples/upgradable-example-l1/scripts/upgrade-box-uups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as hre from 'hardhat';

async function main() {
const Box = await hre.ethers.getContractFactory("BoxUups");
const box = await hre.upgrades.deployProxy(Box,[42],{ initializer: 'initialize'});
const box = await hre.upgrades.deployProxy(Box,[42],{ initializer: 'initialize' });
await box.waitForDeployment()
console.info("Box deployed address: " + await box.getAddress())

Expand Down
2 changes: 1 addition & 1 deletion examples/upgradable-example/contracts/BoxUups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract BoxUups is Initializable, UUPSUpgradeable, OwnableUpgradeable {

function initialize(uint256 initValue) public initializer {
value = initValue;
__Ownable_init();
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
}

Expand Down
6 changes: 4 additions & 2 deletions examples/upgradable-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"clean": "rimraf dist"
},
"devDependencies": {
"@openzeppelin/contracts": "^4.9.2",
"@openzeppelin/contracts": "^5.0.2",
"@types/node": "^18.11.17",
"@typescript-eslint/eslint-plugin": "6.13.1",
"@typescript-eslint/parser": "6.13.1",
Expand All @@ -38,7 +38,9 @@
"@matterlabs/hardhat-zksync-solc": "workspace:^",
"@matterlabs/hardhat-zksync-upgradable": "workspace:^",
"@matterlabs/hardhat-zksync-verify": "workspace:^",
"@openzeppelin/contracts-upgradeable": "^4.9.2",
"@nomicfoundation/hardhat-ethers": "3.0.6",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@openzeppelin/hardhat-upgrades": "^3.2.1",
"chalk": "^4.1.2",
"zksync": "^0.13.1"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/upgradable-example/scripts/deploy-box-uups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async function main() {
const deployer = new Deployer(hre, zkWallet);

const contract = await deployer.loadArtifact(contractName);
const box = await hre.upgrades.deployProxy(deployer.zkWallet, contract, [42], { initializer: 'initialize' });
const box = await hre.upgrades.deployProxy(deployer.zkWallet, contract, [42], { initializer: 'initialize'});

await box.waitForDeployment();

Expand Down
5 changes: 3 additions & 2 deletions packages/hardhat-zksync-upgradable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ const config: HardhatUserConfig = {

### 🕹 Command list

`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>]`
`yarn hardhat deploy-zksync:proxy --contract-name <contract name or FQN> [<constructor arguments>] [--constructor-args <javascript module name>] [--initializer <initialize method>] [--no-compile] [--initial-owner <initial owner>] [--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.
Expand All @@ -293,7 +293,7 @@ If the UUPS proxy is chosen, it will deploy implementation and proxy.
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>] [--initializer <initialize method>] [--deployment-type-impl <deployment type>] [--salt-impl <salt>] [--deployment-type-proxy <deployment type>] [--salt-proxy <salt>] [--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>] [--initial-owner <initial owner>] [--no-compile]`

When executed, this command deploys the provided implementation, beacon and proxy on the specified network, using the provided contract constructor arguments.

Expand All @@ -319,6 +319,7 @@ 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 specify inital contract owner, add `--initial-owner` argument, e.g `hardhat deploy-zksync:beacon --contract-name Contract --initial-owner 0xa61464658AfeAf65CccaaFD3a512b69A83B77618`. If this argument is ommited wallet address will be used.
- 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`
Expand Down
10 changes: 6 additions & 4 deletions packages/hardhat-zksync-upgradable/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
"@matterlabs/hardhat-zksync-deploy": "workspace:^",
"@matterlabs/hardhat-zksync-solc": "workspace:^",
"@matterlabs/hardhat-zksync-ethers": "workspace:^",
"@openzeppelin/contracts-hardhat-zksync-upgradable": "npm:@openzeppelin/contracts@^4.9.2",
"@openzeppelin/hardhat-upgrades": "~2.5.1",
"@openzeppelin/upgrades-core": "^1.31.3",
"@openzeppelin/defender-admin-client": "^1.52.0",
"@openzeppelin/contracts-hardhat-zksync-upgradable": "npm:@openzeppelin/contracts@^5.0.2",
"@openzeppelin/hardhat-upgrades": "^3.2.1",
"@openzeppelin/upgrades-core": "^1.37.0",
"@openzeppelin/defender-sdk-base-client": "^1.10.0",
"@openzeppelin/defender-sdk-deploy-client": "^1.10.0",
"@openzeppelin/defender-sdk-network-client": "^1.10.0",
"chalk": "^4.1.2",
"compare-versions": "^6.1.0",
"ethereumjs-util": "^7.1.5",
Expand Down
62 changes: 14 additions & 48 deletions packages/hardhat-zksync-upgradable/src/admin.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,28 @@
import chalk from 'chalk';
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import { getAdminAddress } from '@openzeppelin/upgrades-core';
import { Wallet, Contract } from 'zksync-ethers';
import { Manifest } from './core/manifest';
import { getAdminFactory } from './proxy-deployment/deploy-proxy-admin';
import { ZkSyncUpgradablePluginError } from './errors';
import { Wallet } from 'zksync-ethers';
import { attachProxyAdminV4 } from './utils/attach-abi';

export type ChangeAdminFunction = (proxyAddress: string, newAdmin: string, wallet: Wallet) => Promise<void>;
export type TransferProxyAdminOwnershipFunction = (newOwner: string, wallet: Wallet) => Promise<void>;
export type GetInstanceFunction = (wallet: Wallet) => Promise<Contract>;
export type TransferProxyAdminOwnershipFunction = (
proxyAddress: string,
newOwner: string,
wallet: Wallet,
) => Promise<void>;

export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment): ChangeAdminFunction {
export function makeChangeProxyAdmin(): ChangeAdminFunction {
return async function changeProxyAdmin(proxyAddress, newAdmin, wallet: Wallet) {
const proxyAdminManifest = await getManifestAdmin(hre, wallet);

const proxyAdminAddress = await getAdminAddress(wallet.provider, proxyAddress);

if ((await proxyAdminManifest.getAddress()) !== proxyAdminAddress) {
throw new ZkSyncUpgradablePluginError('Proxy admin is not the one registered in the network manifest');
} else if ((await proxyAdminManifest.getAddress()) !== newAdmin) {
await proxyAdminManifest.changeProxyAdmin(proxyAddress, newAdmin);
}
const admin = await attachProxyAdminV4(proxyAdminAddress, wallet);
await admin.changeProxyAdmin(proxyAddress, newAdmin);
};
}

export function makeTransferProxyAdminOwnership(hre: HardhatRuntimeEnvironment): TransferProxyAdminOwnershipFunction {
return async function transferProxyAdminOwnership(newOwner, wallet: Wallet) {
const admin = await getManifestAdmin(hre, wallet);
export function makeTransferProxyAdminOwnership(): TransferProxyAdminOwnershipFunction {
return async function transferProxyAdminOwnership(proxyAddress: string, newOwner, wallet: Wallet) {
const proxyAdminAddress = await getAdminAddress(wallet.provider, proxyAddress);

const admin = await attachProxyAdminV4(proxyAdminAddress, wallet);
await admin.transferOwnership(newOwner);

const manifest = await Manifest.forNetwork(wallet.provider);
const { proxies } = await manifest.read();
for (const { address, kind } of proxies) {
if ((await admin.getAddress()) === (await getAdminAddress(wallet.provider, address))) {
console.info(chalk.green(`${address} (${kind}) proxy ownership transfered through admin proxy`));
} else {
console.info(chalk.red(`${address} (${kind}) proxy ownership not affected by admin proxy`));
}
}
};
}

export function makeGetInstanceFunction(hre: HardhatRuntimeEnvironment): GetInstanceFunction {
return async function getInstance(wallet: Wallet) {
return await getManifestAdmin(hre, wallet);
};
}

export async function getManifestAdmin(hre: HardhatRuntimeEnvironment, wallet: Wallet): Promise<Contract> {
const manifest = await Manifest.forNetwork(wallet.provider);
const manifestAdmin = await manifest.getAdmin();
const proxyAdminAddress = manifestAdmin?.address;

if (proxyAdminAddress === undefined) {
throw new ZkSyncUpgradablePluginError('No ProxyAdmin was found in the network manifest');
}

const adminFactory = await getAdminFactory(hre, wallet);
return adminFactory.attach(proxyAdminAddress);
}
18 changes: 9 additions & 9 deletions packages/hardhat-zksync-upgradable/src/core/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,25 +253,25 @@ function* getStateVariableErrors(
): Generator<ValidationErrorWithName> {
for (const varDecl of contractDef.nodes) {
if (isNodeType('VariableDeclaration', varDecl)) {
if (!varDecl.constant && !isNullish(varDecl.value)) {
if (varDecl.mutability === 'immutable') {
if (
!skipCheck('state-variable-assignment', contractDef) &&
!skipCheck('state-variable-assignment', varDecl)
!skipCheck('state-variable-immutable', contractDef) &&
!skipCheck('state-variable-immutable', varDecl)
) {
yield {
kind: 'state-variable-assignment',
kind: 'state-variable-immutable',
name: varDecl.name,
src: decodeSrc(varDecl),
};
}
}
if (varDecl.mutability === 'immutable') {
} else if (!varDecl.constant && !isNullish(varDecl.value)) {
// Assignments are only a concern for non-immutable variables
if (
!skipCheck('state-variable-immutable', contractDef) &&
!skipCheck('state-variable-immutable', varDecl)
!skipCheck('state-variable-assignment', contractDef) &&
!skipCheck('state-variable-assignment', varDecl)
) {
yield {
kind: 'state-variable-immutable',
kind: 'state-variable-assignment',
name: varDecl.name,
src: decodeSrc(varDecl),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,18 @@ export class ZkSyncGenerator implements Generator {
const { makeDeployBeacon } = require('./proxy-deployment/deploy-beacon');
const { makeDeployBeaconProxy } = require('./proxy-deployment/deploy-beacon-proxy');
const { makeUpgradeBeacon } = require('./proxy-upgrade/upgrade-beacon');
const { makeDeployProxyAdmin } = require('./proxy-deployment/deploy-proxy-admin');
const { makeEstimateGasProxy } = require('./gas-estimation/estimate-gas-proxy');
const { makeEstimateGasBeacon } = require('./gas-estimation/estimate-gas-beacon');
const { makeEstimateGasBeaconProxy } = require('./gas-estimation/estimate-gas-beacon-proxy');
const { makeGetInstanceFunction, makeChangeProxyAdmin, makeTransferProxyAdminOwnership } = require('./admin');
const { makeChangeProxyAdmin, makeTransferProxyAdminOwnership } = require('./admin');
return {
deployProxy: wrapMakeFunction(this._hre, makeDeployProxy(this._hre)),
upgradeProxy: wrapMakeFunction(this._hre, makeUpgradeProxy(this._hre)),
validateImplementation: wrapMakeFunction(this._hre, makeValidateImplementation(this._hre)),
deployBeacon: wrapMakeFunction(this._hre, makeDeployBeacon(this._hre)),
deployBeaconProxy: wrapMakeFunction(this._hre, makeDeployBeaconProxy(this._hre)),
upgradeBeacon: wrapMakeFunction(this._hre, makeUpgradeBeacon(this._hre)),
deployProxyAdmin: wrapMakeFunction(this._hre, makeDeployProxyAdmin(this._hre)),
admin: {
getInstance: wrapMakeFunction(this._hre, makeGetInstanceFunction(this._hre)),
changeProxyAdmin: wrapMakeFunction(this._hre, makeChangeProxyAdmin(this._hre)),
transferProxyAdminOwnership: wrapMakeFunction(this._hre, makeTransferProxyAdminOwnership(this._hre)),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import chalk from 'chalk';
import assert from 'assert';
import path from 'path';
import { Deployer } from '@matterlabs/hardhat-zksync-deploy';
import { DeployProxyOptions } from '../utils/options';
import { ZkSyncUpgradablePluginError } from '../errors';
import { convertGasPriceToEth } from '../utils/utils-general';
import { BEACON_PROXY_JSON } from '../constants';

import { getUpgradableContracts } from '../utils';
import { getBeaconProxyArtifact } from '../utils/factories';
import { getMockedBeaconData } from './estimate-gas-beacon';

export type EstimateBeaconGasFunction = (
Expand All @@ -27,11 +24,7 @@ export function makeEstimateGasBeaconProxy(hre: HardhatRuntimeEnvironment): Esti
) {
const { mockedBeaconAddress, data } = await getMockedBeaconData(deployer, hre, args, opts);

const beaconProxyPath = (await hre.artifacts.getArtifactPaths()).find((artifactPath) =>
artifactPath.includes(path.sep + getUpgradableContracts().BeaconProxy + path.sep + BEACON_PROXY_JSON),
);
assert(beaconProxyPath, 'Beacon proxy artifact not found');
const beaconProxyContract = await import(beaconProxyPath);
const beaconProxyContract = await getBeaconProxyArtifact(hre);

try {
const beaconProxyGasCost = await deployer.estimateDeployFee(beaconProxyContract, [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import * as ethers from 'ethers';
import chalk from 'chalk';
import assert from 'assert';
import path from 'path';

import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-deploy/src/types';
import { Deployer } from '@matterlabs/hardhat-zksync-deploy';
import { DeployProxyOptions } from '../utils/options';
import { ZkSyncUpgradablePluginError } from '../errors';
import { convertGasPriceToEth, getInitializerData } from '../utils/utils-general';
import { UPGRADABLE_BEACON_JSON } from '../constants';

import { getAdminArtifact } from '../proxy-deployment/deploy-proxy-admin';
import { getChainId } from '../core/provider';
import { getUpgradableContracts } from '../utils';
import { getProxyAdminArtifact, getUpgradableBeaconArtifact } from '../utils/factories';

export type EstimateGasFunction = (
deployer: Deployer,
Expand All @@ -35,7 +32,7 @@ export async function getMockedBeaconData(
}

const mockedBeaconAddress = await getDeployedBeaconAddress(deployer);
const mockArtifact = await getAdminArtifact(hre);
const mockArtifact = await getProxyAdminArtifact(hre);
const data = getInitializerData(new ethers.Interface(mockArtifact.abi), args, opts.initializer);

return { mockedBeaconAddress, data };
Expand Down Expand Up @@ -74,15 +71,13 @@ export function makeEstimateGasBeacon(hre: HardhatRuntimeEnvironment): EstimateG
),
);
}

const upgradableBeaconPath = (await hre.artifacts.getArtifactPaths()).find((x) =>
x.includes(path.sep + getUpgradableContracts().UpgradeableBeacon + path.sep + UPGRADABLE_BEACON_JSON),
);
assert(upgradableBeaconPath, 'Upgradable beacon artifact not found');
const upgradeableBeaconContract = await import(upgradableBeaconPath);
const upgradeableBeaconContract = await getUpgradableBeaconArtifact(hre);

try {
beaconGasCost = await deployer.estimateDeployFee(upgradeableBeaconContract, [mockedBeaconAddress]);
beaconGasCost = await deployer.estimateDeployFee(upgradeableBeaconContract, [
mockedBeaconAddress,
opts.initialOwner ?? deployer.zkWallet.address,
]);
if (!quiet) {
console.info(
chalk.cyan(
Expand Down
Loading

0 comments on commit ebb668b

Please sign in to comment.