Skip to content

Commit

Permalink
Merge pull request #2 from txfusion/mpavlovic-txfusion-l2-bridge
Browse files Browse the repository at this point in the history
ZkSync wstETH bridge and governance
  • Loading branch information
ljankovic-txfusion authored Jun 16, 2023
2 parents 4de56a6 + 9243961 commit 1c2492e
Show file tree
Hide file tree
Showing 35 changed files with 34,481 additions and 11,802 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ out

#Hardhat files
cache
/artifacts
artifacts
cache-zk
artifacts-zk

.DS_Store
.vscode
Empty file removed contracts/zksync/stubs/.gitkeep
Empty file.
6,269 changes: 4,940 additions & 1,329 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@matterlabs/zksync-contracts": "^0.6.1",
"@nomiclabs/hardhat-ethers": "^2.0.6",
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@nomiclabs/hardhat-waffle": "^2.0.3",
Expand Down Expand Up @@ -77,9 +78,8 @@
"@eth-optimism/sdk": "^1.1.7",
"@ethersproject/providers": "^5.6.8",
"@lidofinance/evm-script-decoder": "^0.2.2",
"@matterlabs/hardhat-zksync-solc": "^0.3.17",
"@matterlabs/zksync-contracts": "^0.6.1",
"@openzeppelin/contracts": "^4.9.1",
"@openzeppelin/contracts": "^4.6.0",
"@openzeppelin/contracts-upgradeable": "^4.6.0",
"arb-ts": "^1.0.2",
"chalk": "4.1.2"
}
Expand Down
21,274 changes: 10,874 additions & 10,400 deletions yarn.lock

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions zksync/common/BridgeableTokens.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2022 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;

/// @author psirex
/// @notice Contains the logic for validation of tokens used in the bridging process
contract BridgeableTokens {
/// @notice Address of the bridged token in the L1 chain
address public immutable l1Token;

/// @notice Address of the token minted on the L2 chain when token bridged
address public immutable l2Token;

/// @param l1Token_ Address of the bridged token in the L1 chain
/// @param l2Token_ Address of the token minted on the L2 chain when token bridged
constructor(address l1Token_, address l2Token_) {
l1Token = l1Token_;
l2Token = l2Token_;
}

/// @dev Validates that passed l1Token_ is supported by the bridge
modifier onlySupportedL1Token(address l1Token_) {
if (l1Token_ != l1Token) {
revert ErrorUnsupportedL1Token();
}
_;
}

/// @dev Validates that passed l2Token_ is supported by the bridge
modifier onlySupportedL2Token(address l2Token_) {
if (l2Token_ != l2Token) {
revert ErrorUnsupportedL2Token();
}
_;
}

/// @dev validates that account_ is not zero address
modifier onlyNonZeroAccount(address account_) {
if (account_ == address(0)) {
revert ErrorAccountIsZeroAddress();
}
_;
}

error ErrorUnsupportedL1Token();
error ErrorUnsupportedL2Token();
error ErrorAccountIsZeroAddress();
}
50 changes: 50 additions & 0 deletions zksync/common/BridgeableTokensUpgradable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2022 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/// @notice Upgadeable variant of contract that contains the logic for validation of tokens used in the bridging process
contract BridgeableTokensUpgradable is Initializable {
/// @notice Address of the bridged token in the L1 chain
address public l1Token;

/// @notice Address of the token minted on the L2 chain when token bridged
address public l2Token;

/// @param l1Token_ Address of the bridged token in the L1 chain
/// @param l2Token_ Address of the token minted on the L2 chain when token bridged
function __BridgeableTokens_init(address l1Token_, address l2Token_) internal onlyInitializing {
l1Token = l1Token_;
l2Token = l2Token_;
}

/// @dev Validates that passed l1Token_ is supported by the bridge
modifier onlySupportedL1Token(address l1Token_) {
if (l1Token_ != l1Token) {
revert ErrorUnsupportedL1Token();
}
_;
}

/// @dev Validates that passed l2Token_ is supported by the bridge
modifier onlySupportedL2Token(address l2Token_) {
if (l2Token_ != l2Token) {
revert ErrorUnsupportedL2Token();
}
_;
}

/// @dev validates that account_ is not zero address
modifier onlyNonZeroAccount(address account_) {
if (account_ == address(0)) {
revert ErrorAccountIsZeroAddress();
}
_;
}

error ErrorUnsupportedL1Token();
error ErrorUnsupportedL2Token();
error ErrorAccountIsZeroAddress();
}
135 changes: 135 additions & 0 deletions zksync/common/BridgingManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: 2022 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

/// @author psirex
/// @notice Contains administrative methods to retrieve and control the state of the bridging
contract BridgingManager is AccessControl {
/// @dev Stores the state of the bridging
/// @param isInitialized Shows whether the contract is initialized or not
/// @param isDepositsEnabled Stores the state of the deposits
/// @param isWithdrawalsEnabled Stores the state of the withdrawals
struct State {
bool isInitialized;
bool isDepositsEnabled;
bool isWithdrawalsEnabled;
}

bytes32 public constant DEPOSITS_ENABLER_ROLE =
keccak256("BridgingManager.DEPOSITS_ENABLER_ROLE");
bytes32 public constant DEPOSITS_DISABLER_ROLE =
keccak256("BridgingManager.DEPOSITS_DISABLER_ROLE");
bytes32 public constant WITHDRAWALS_ENABLER_ROLE =
keccak256("BridgingManager.WITHDRAWALS_ENABLER_ROLE");
bytes32 public constant WITHDRAWALS_DISABLER_ROLE =
keccak256("BridgingManager.WITHDRAWALS_DISABLER_ROLE");

/// @dev The location of the slot with State
bytes32 private constant STATE_SLOT =
keccak256("BridgingManager.bridgingState");

/// @notice Initializes the contract to grant DEFAULT_ADMIN_ROLE to the admin_ address
/// @dev This method might be called only once
/// @param admin_ Address of the account to grant the DEFAULT_ADMIN_ROLE
function initialize(address admin_) external {
State storage s = _loadState();
if (s.isInitialized) {
revert ErrorAlreadyInitialized();
}
_setupRole(DEFAULT_ADMIN_ROLE, admin_);
s.isInitialized = true;
emit Initialized(admin_);
}

/// @notice Returns whether the contract is initialized or not
function isInitialized() public view returns (bool) {
return _loadState().isInitialized;
}

/// @notice Returns whether the deposits are enabled or not
function isDepositsEnabled() public view returns (bool) {
return _loadState().isDepositsEnabled;
}

/// @notice Returns whether the withdrawals are enabled or not
function isWithdrawalsEnabled() public view returns (bool) {
return _loadState().isWithdrawalsEnabled;
}

/// @notice Enables the deposits if they are disabled
function enableDeposits() external onlyRole(DEPOSITS_ENABLER_ROLE) {
if (isDepositsEnabled()) {
revert ErrorDepositsEnabled();
}
_loadState().isDepositsEnabled = true;
emit DepositsEnabled(msg.sender);
}

/// @notice Disables the deposits if they aren't disabled yet
function disableDeposits()
external
whenDepositsEnabled
onlyRole(DEPOSITS_DISABLER_ROLE)
{
_loadState().isDepositsEnabled = false;
emit DepositsDisabled(msg.sender);
}

/// @notice Enables the withdrawals if they are disabled
function enableWithdrawals() external onlyRole(WITHDRAWALS_ENABLER_ROLE) {
if (isWithdrawalsEnabled()) {
revert ErrorWithdrawalsEnabled();
}
_loadState().isWithdrawalsEnabled = true;
emit WithdrawalsEnabled(msg.sender);
}

/// @notice Disables the withdrawals if they aren't disabled yet
function disableWithdrawals()
external
whenWithdrawalsEnabled
onlyRole(WITHDRAWALS_DISABLER_ROLE)
{
_loadState().isWithdrawalsEnabled = false;
emit WithdrawalsDisabled(msg.sender);
}

/// @dev Returns the reference to the slot with State struct
function _loadState() private pure returns (State storage r) {
bytes32 slot = STATE_SLOT;
assembly {
r.slot := slot
}
}

/// @dev Validates that deposits are enabled
modifier whenDepositsEnabled() {
if (!isDepositsEnabled()) {
revert ErrorDepositsDisabled();
}
_;
}

/// @dev Validates that withdrawals are enabled
modifier whenWithdrawalsEnabled() {
if (!isWithdrawalsEnabled()) {
revert ErrorWithdrawalsDisabled();
}
_;
}

event DepositsEnabled(address indexed enabler);
event DepositsDisabled(address indexed disabler);
event WithdrawalsEnabled(address indexed enabler);
event WithdrawalsDisabled(address indexed disabler);
event Initialized(address indexed admin);

error ErrorDepositsEnabled();
error ErrorDepositsDisabled();
error ErrorWithdrawalsEnabled();
error ErrorWithdrawalsDisabled();
error ErrorAlreadyInitialized();
}
93 changes: 93 additions & 0 deletions zksync/common/proxy/OssifiableProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2022 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

/// @notice An ossifiable proxy contract. Extends the ERC1967Proxy contract by
/// adding admin functionality
contract OssifiableProxy is ERC1967Proxy {
/// @dev Initializes the upgradeable proxy with the initial implementation and admin
/// @param implementation_ Address of the implementation
/// @param admin_ Address of the admin of the proxy
/// @param data_ Data used in a delegate call to implementation. The delegate call will be
/// skipped if the data is empty bytes
constructor(
address implementation_,
address admin_,
bytes memory data_
) ERC1967Proxy(implementation_, data_) {
_changeAdmin(admin_);
}

/// @notice Returns the current admin of the proxy
function proxy__getAdmin() external view returns (address) {
return _getAdmin();
}

/// @notice Returns the current implementation address
function proxy__getImplementation() external view returns (address) {
return _implementation();
}

/// @notice Returns whether the implementation is locked forever
function proxy__getIsOssified() external view returns (bool) {
return _getAdmin() == address(0);
}

/// @notice Allows to transfer admin rights to zero address and prevent future
/// upgrades of the proxy
function proxy__ossify() external onlyAdmin {
address prevAdmin = _getAdmin();
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = address(0);
emit AdminChanged(prevAdmin, address(0));
emit ProxyOssified();
}

/// @notice Changes the admin of the proxy
/// @param newAdmin_ Address of the new admin
function proxy__changeAdmin(address newAdmin_) external onlyAdmin {
_changeAdmin(newAdmin_);
}

/// @notice Upgrades the implementation of the proxy
/// @param newImplementation_ Address of the new implementation
function proxy__upgradeTo(address newImplementation_) external onlyAdmin {
_upgradeTo(newImplementation_);
}

/// @notice Upgrades the proxy to a new implementation, optionally performing an additional
/// setup call.
/// @param newImplementation_ Address of the new implementation
/// @param setupCalldata_ Data for the setup call. The call is skipped if setupCalldata_ is
/// empty and forceCall_ is false
/// @param forceCall_ Forces make delegate call to the implementation even with empty data_
function proxy__upgradeToAndCall(
address newImplementation_,
bytes memory setupCalldata_,
bool forceCall_
) external onlyAdmin {
_upgradeToAndCall(newImplementation_, setupCalldata_, forceCall_);
}

/// @dev Validates that proxy is not ossified and that method is called by the admin
/// of the proxy
modifier onlyAdmin() {
address admin = _getAdmin();
if (admin == address(0)) {
revert ErrorProxyIsOssified();
}
if (admin != msg.sender) {
revert ErrorNotAdmin();
}
_;
}

event ProxyOssified();

error ErrorNotAdmin();
error ErrorProxyIsOssified();
}
Loading

0 comments on commit 1c2492e

Please sign in to comment.