Skip to content

Commit

Permalink
Add the ability to reinitialize erc20 token (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
StanislavBreadless committed Nov 28, 2023
1 parent d0a8e5e commit 518bfff
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 74 deletions.
16 changes: 15 additions & 1 deletion .github/workflows/ci.yml
Expand Up @@ -211,7 +211,7 @@ jobs:
run: forge test

test-hardhat-l2:
needs: [build-l2, lint-l2]
needs: [build-l1, build-l2, lint-l2]
runs-on: ubuntu-latest

defaults:
Expand All @@ -237,6 +237,20 @@ jobs:
- name: Install dependencies
run: yarn install

- name: Install L1 dependencies
working-directory: ethereum
run: yarn install

- name: Restore L1 artifacts cache
uses: actions/cache/restore@v3
with:
fail-on-cache-miss: true
key: artifacts-${{ github.sha }}
path: |
ethereum/artifacts
ethereum/cache
ethereum/typechain
- name: Restore artifacts cache
uses: actions/cache/restore@v3
with:
Expand Down
4 changes: 2 additions & 2 deletions ethereum/package.json
Expand Up @@ -9,8 +9,8 @@
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@nomiclabs/hardhat-solpp": "^2.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.0",
"@openzeppelin/contracts": "4.8.0",
"@openzeppelin/contracts-upgradeable": "4.8.0",
"@openzeppelin/contracts": "4.9.2",
"@openzeppelin/contracts-upgradeable": "4.9.2",
"@typechain/ethers-v5": "^2.0.0",
"@types/argparse": "^1.0.36",
"@types/chai": "^4.2.21",
Expand Down
48 changes: 48 additions & 0 deletions ethereum/scripts/utils.ts
Expand Up @@ -151,3 +151,51 @@ export function getTokens(network: string): L1Token[] {
})
);
}

export interface DeployedAddresses {
ZkSync: {
MailboxFacet: string;
AdminFacet: string;
ExecutorFacet: string;
GettersFacet: string;
Verifier: string;
DiamondInit: string;
DiamondUpgradeInit: string;
DefaultUpgrade: string;
DiamondProxy: string;
};
Bridges: {
ERC20BridgeImplementation: string;
ERC20BridgeProxy: string;
WethBridgeImplementation: string;
WethBridgeProxy: string;
};
Governance: string;
ValidatorTimeLock: string;
Create2Factory: string;
}

export function deployedAddressesFromEnv(): DeployedAddresses {
return {
ZkSync: {
MailboxFacet: getAddressFromEnv("CONTRACTS_MAILBOX_FACET_ADDR"),
AdminFacet: getAddressFromEnv("CONTRACTS_ADMIN_FACET_ADDR"),
ExecutorFacet: getAddressFromEnv("CONTRACTS_EXECUTOR_FACET_ADDR"),
GettersFacet: getAddressFromEnv("CONTRACTS_GETTERS_FACET_ADDR"),
DiamondInit: getAddressFromEnv("CONTRACTS_DIAMOND_INIT_ADDR"),
DiamondUpgradeInit: getAddressFromEnv("CONTRACTS_DIAMOND_UPGRADE_INIT_ADDR"),
DefaultUpgrade: getAddressFromEnv("CONTRACTS_DEFAULT_UPGRADE_ADDR"),
DiamondProxy: getAddressFromEnv("CONTRACTS_DIAMOND_PROXY_ADDR"),
Verifier: getAddressFromEnv("CONTRACTS_VERIFIER_ADDR"),
},
Bridges: {
ERC20BridgeImplementation: getAddressFromEnv("CONTRACTS_L1_ERC20_BRIDGE_IMPL_ADDR"),
ERC20BridgeProxy: getAddressFromEnv("CONTRACTS_L1_ERC20_BRIDGE_PROXY_ADDR"),
WethBridgeImplementation: getAddressFromEnv("CONTRACTS_L1_WETH_BRIDGE_IMPL_ADDR"),
WethBridgeProxy: getAddressFromEnv("CONTRACTS_L1_WETH_BRIDGE_PROXY_ADDR"),
},
Create2Factory: getAddressFromEnv("CONTRACTS_CREATE2_FACTORY_ADDR"),
ValidatorTimeLock: getAddressFromEnv("CONTRACTS_VALIDATOR_TIMELOCK_ADDR"),
Governance: getAddressFromEnv("CONTRACTS_GOVERNANCE_ADDR"),
};
}
50 changes: 2 additions & 48 deletions ethereum/src.ts/deploy.ts
Expand Up @@ -11,6 +11,7 @@ import { L1WethBridgeFactory } from "../typechain/L1WethBridgeFactory";
import { ValidatorTimelockFactory } from "../typechain/ValidatorTimelockFactory";
import { SingletonFactoryFactory } from "../typechain/SingletonFactoryFactory";
import { TransparentUpgradeableProxyFactory } from "../typechain/TransparentUpgradeableProxyFactory";
import type { DeployedAddresses } from "../scripts/utils";
import {
readSystemContractsBytecode,
hashL2Bytecode,
Expand All @@ -19,67 +20,20 @@ import {
getNumberFromEnv,
readBatchBootloaderBytecode,
getTokens,
deployedAddressesFromEnv,
} from "../scripts/utils";
import { deployViaCreate2 } from "./deploy-utils";
import { IGovernanceFactory } from "../typechain/IGovernanceFactory";

const L2_BOOTLOADER_BYTECODE_HASH = hexlify(hashL2Bytecode(readBatchBootloaderBytecode()));
const L2_DEFAULT_ACCOUNT_BYTECODE_HASH = hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount")));

export interface DeployedAddresses {
ZkSync: {
MailboxFacet: string;
AdminFacet: string;
ExecutorFacet: string;
GettersFacet: string;
Verifier: string;
DiamondInit: string;
DiamondUpgradeInit: string;
DefaultUpgrade: string;
DiamondProxy: string;
};
Bridges: {
ERC20BridgeImplementation: string;
ERC20BridgeProxy: string;
WethBridgeImplementation: string;
WethBridgeProxy: string;
};
Governance: string;
ValidatorTimeLock: string;
Create2Factory: string;
}

export interface DeployerConfig {
deployWallet: Wallet;
ownerAddress?: string;
verbose?: boolean;
}

export function deployedAddressesFromEnv(): DeployedAddresses {
return {
ZkSync: {
MailboxFacet: getAddressFromEnv("CONTRACTS_MAILBOX_FACET_ADDR"),
AdminFacet: getAddressFromEnv("CONTRACTS_ADMIN_FACET_ADDR"),
ExecutorFacet: getAddressFromEnv("CONTRACTS_EXECUTOR_FACET_ADDR"),
GettersFacet: getAddressFromEnv("CONTRACTS_GETTERS_FACET_ADDR"),
DiamondInit: getAddressFromEnv("CONTRACTS_DIAMOND_INIT_ADDR"),
DiamondUpgradeInit: getAddressFromEnv("CONTRACTS_DIAMOND_UPGRADE_INIT_ADDR"),
DefaultUpgrade: getAddressFromEnv("CONTRACTS_DEFAULT_UPGRADE_ADDR"),
DiamondProxy: getAddressFromEnv("CONTRACTS_DIAMOND_PROXY_ADDR"),
Verifier: getAddressFromEnv("CONTRACTS_VERIFIER_ADDR"),
},
Bridges: {
ERC20BridgeImplementation: getAddressFromEnv("CONTRACTS_L1_ERC20_BRIDGE_IMPL_ADDR"),
ERC20BridgeProxy: getAddressFromEnv("CONTRACTS_L1_ERC20_BRIDGE_PROXY_ADDR"),
WethBridgeImplementation: getAddressFromEnv("CONTRACTS_L1_WETH_BRIDGE_IMPL_ADDR"),
WethBridgeProxy: getAddressFromEnv("CONTRACTS_L1_WETH_BRIDGE_PROXY_ADDR"),
},
Create2Factory: getAddressFromEnv("CONTRACTS_CREATE2_FACTORY_ADDR"),
ValidatorTimeLock: getAddressFromEnv("CONTRACTS_VALIDATOR_TIMELOCK_ADDR"),
Governance: getAddressFromEnv("CONTRACTS_GOVERNANCE_ADDR"),
};
}

export class Deployer {
public addresses: DeployedAddresses;
private deployWallet: Wallet;
Expand Down
18 changes: 9 additions & 9 deletions ethereum/yarn.lock
Expand Up @@ -866,15 +866,15 @@
"@types/sinon-chai" "^3.2.3"
"@types/web3" "1.0.19"

"@openzeppelin/contracts-upgradeable@4.8.0":
version "4.8.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.0.tgz#26688982f46969018e3ed3199e72a07c8d114275"
integrity sha512-5GeFgqMiDlqGT8EdORadp1ntGF0qzWZLmEY7Wbp/yVhN7/B3NNzCxujuI77ktlyG81N3CUZP8cZe3ZAQ/cW10w==

"@openzeppelin/contracts@4.8.0":
version "4.8.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109"
integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw==
"@openzeppelin/contracts-upgradeable@4.9.2":
version "4.9.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.2.tgz#a817c75688f8daede420052fbcb34e52482e769e"
integrity sha512-siviV3PZV/fHfPaoIC51rf1Jb6iElkYWnNYZ0leO23/ukXuvOyoC/ahy8jqiV7g+++9Nuo3n/rk5ajSN/+d/Sg==

"@openzeppelin/contracts@4.9.2":
version "4.9.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.2.tgz#1cb2d5e4d3360141a17dbc45094a8cad6aac16c1"
integrity sha512-mO+y6JaqXjWeMh9glYVzVu8HYPGknAAnWyxTRhGeckOruyXQMNnlcW6w/Dx9ftLeIQk6N+ZJFuVmTwF7lEIFrg==

"@pkgr/utils@^2.3.1":
version "2.4.2"
Expand Down
40 changes: 39 additions & 1 deletion zksync/contracts/bridge/L2StandardERC20.sol
Expand Up @@ -3,12 +3,15 @@
pragma solidity 0.8.20;

import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";

import "./interfaces/IL2StandardToken.sol";

/// @author Matter Labs
/// @custom:security-contact security@matterlabs.dev
/// @notice The ERC20 token implementation, that is used in the "default" ERC20 bridge
contract L2StandardERC20 is ERC20PermitUpgradeable, IL2StandardToken {
contract L2StandardERC20 is ERC20PermitUpgradeable, IL2StandardToken, ERC1967Upgrade {
/// @dev Describes whether there is a specific getter in the token.
/// @notice Used to explicitly separate which getters the token has and which it does not.
/// @notice Different tokens in L1 can implement or not implement getter function as `name`/`symbol`/`decimals`,
Expand Down Expand Up @@ -97,11 +100,46 @@ contract L2StandardERC20 is ERC20PermitUpgradeable, IL2StandardToken {
emit BridgeInitialize(_l1Address, decodedName, decodedSymbol, decimals_);
}

/// @notice A method to be called by the governor to update the token's metadata.
/// @param _availableGetters The getters that the token has.
/// @param _newName The new name of the token.
/// @param _newSymbol The new symbol of the token.
/// @param _newDecimals The new decimals of the token.
/// @param _version The version of the token that will be initialized.
/// @dev The _version must be exactly the version higher by 1 than the current version. This is needed
/// to ensure that the governor can not accidentally disable future reinitialization of the token.
function reinitializeToken(
ERC20Getters calldata _availableGetters,
string memory _newName,
string memory _newSymbol,
uint8 _newDecimals,
uint8 _version
) external onlyNextVersion(_version) reinitializer(_version) {
// It is expected that this token is deployed as a beacon proxy, so we'll
// allow the governor of the beacon to reinitialize the token.
address beaconAddress = _getBeacon();
require(msg.sender == UpgradeableBeacon(beaconAddress).owner(), "tt");

__ERC20_init_unchained(_newName, _newSymbol);
__ERC20Permit_init(_newName);
decimals_ = _newDecimals;
availableGetters = _availableGetters;

emit BridgeInitialize(l1Address, _newName, _newSymbol, _newDecimals);
}

modifier onlyBridge() {
require(msg.sender == l2Bridge, "xnt"); // Only L2 bridge can call this method
_;
}

modifier onlyNextVersion(uint8 _version) {
// The version should be incremented by 1. Otherwise, the governor risks disabling
// future reinitialization of the token by providing too large a version.
require(_version == _getInitializedVersion() + 1, "v");
_;
}

/// @dev Mint tokens to a given account.
/// @param _to The account that will receive the created tokens.
/// @param _amount The amount that will be created.
Expand Down
7 changes: 4 additions & 3 deletions zksync/package.json
Expand Up @@ -14,8 +14,8 @@
"@nomiclabs/hardhat-ethers": "^2.0.0",
"@nomiclabs/hardhat-etherscan": "^3.1.7",
"@nomiclabs/hardhat-solpp": "^2.0.0",
"@openzeppelin/contracts": "4.6.0",
"@openzeppelin/contracts-upgradeable": "4.6.0",
"@openzeppelin/contracts": "4.9.2",
"@openzeppelin/contracts-upgradeable": "4.9.2",
"@typechain/ethers-v5": "^2.0.0",
"@types/chai": "^4.2.21",
"@types/chai-as-promised": "^7.1.4",
Expand Down Expand Up @@ -58,7 +58,8 @@
"deploy-force-deploy-upgrader": "ts-node src/deployForceDeployUpgrader.ts",
"publish-bridge-preimages": "ts-node src/publish-bridge-preimages.ts",
"deploy-l2-weth": "ts-node src/deployL2Weth.ts",
"upgrade-l2-erc20-contract": "ts-node src/upgradeL2BridgeImpl.ts"
"upgrade-l2-erc20-contract": "ts-node src/upgradeL2BridgeImpl.ts",
"test": "hardhat test --network localhost"
},
"dependencies": {
"dotenv": "^16.0.3"
Expand Down
9 changes: 8 additions & 1 deletion zksync/src/utils.ts
@@ -1,7 +1,7 @@
import { artifacts } from "hardhat";

import { Interface } from "ethers/lib/utils";
import { deployedAddressesFromEnv } from "../../ethereum/src.ts/deploy";
import { deployedAddressesFromEnv } from "../../ethereum/scripts/utils";
import { IZkSyncFactory } from "../../ethereum/typechain/IZkSyncFactory";

import type { BytesLike, Wallet } from "ethers";
Expand All @@ -21,6 +21,13 @@ export function applyL1ToL2Alias(address: string): string {
return ethers.utils.hexlify(ethers.BigNumber.from(address).add(L1_TO_L2_ALIAS_OFFSET).mod(ADDRESS_MODULO));
}

export function unapplyL1ToL2Alias(address: string): string {
// We still add ADDRESS_MODULO to avoid negative numbers
return ethers.utils.hexlify(
ethers.BigNumber.from(address).sub(L1_TO_L2_ALIAS_OFFSET).add(ADDRESS_MODULO).mod(ADDRESS_MODULO)
);
}

export function hashL2Bytecode(bytecode: ethers.BytesLike): Uint8Array {
// For getting the consistent length we first convert the bytecode to UInt8Array
const bytecodeAsArray = ethers.utils.arrayify(bytecode);
Expand Down

0 comments on commit 518bfff

Please sign in to comment.